From e1c45384cb57f51c583e4d2e3018992ad1b72744 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Mon, 23 Feb 2015 11:41:41 +1100 Subject: [PATCH 01/92] Removed old GoonBase folder --- GoonBase/goonbase.lua | 246 --- GoonBase/lua/AchievementManager.lua | 61 - GoonBase/lua/ActionSpooc.lua | 19 - GoonBase/lua/AssetsTweakData.lua | 20 - GoonBase/lua/BlackMarketGUI.lua | 1637 ----------------- GoonBase/lua/BlackMarketManager.lua | 398 ---- GoonBase/lua/BlackMarketTweakData.lua | 13 - GoonBase/lua/CharacterTweakData.lua | 97 - GoonBase/lua/ChatManager.lua | 19 - GoonBase/lua/ContourExt.lua | 24 - GoonBase/lua/CopActionHurt.lua | 13 - GoonBase/lua/CopDamage.lua | 44 - GoonBase/lua/CopInventory.lua | 55 - GoonBase/lua/CoreMenuInput.lua | 45 - GoonBase/lua/CoreMenuItem.lua | 24 - GoonBase/lua/CoreMenuItemSlider.lua | 28 - GoonBase/lua/CoreMenuLogic.lua | 30 - GoonBase/lua/CriminalsManager.lua | 17 - GoonBase/lua/ElementLaserTrigger.lua | 19 - GoonBase/lua/ElementSpawnGrenade.lua | 11 - GoonBase/lua/EnemyManager.lua | 19 - GoonBase/lua/FPCameraPlayerBase.lua | 13 - GoonBase/lua/FragGrenade.lua | 19 - GoonBase/lua/GageAssignmentManager.lua | 13 - GoonBase/lua/GameSetup.lua | 13 - GoonBase/lua/GameStateMachine.lua | 13 - GoonBase/lua/GroupAIManager.lua | 12 - GoonBase/lua/GroupAIStateBase.lua | 20 - GoonBase/lua/GroupAIStateBesiege.lua | 13 - GoonBase/lua/GroupAITweakData.lua | 25 - GoonBase/lua/HUDManager.lua | 40 - GoonBase/lua/HUDManagerPD2.lua | 156 -- GoonBase/lua/InfamyTweakData.lua | 11 - GoonBase/lua/InteractionExt.lua | 19 - GoonBase/lua/JobManager.lua | 13 - GoonBase/lua/LevelsTweakData.lua | 13 - GoonBase/lua/LocalizationManager.lua | 24 - GoonBase/lua/MaskExt.lua | 25 - GoonBase/lua/MenuComponentManager.lua | 13 - GoonBase/lua/MenuItemCustomizeController.lua | 72 - GoonBase/lua/MenuManager.lua | 164 -- GoonBase/lua/MenuNodeGUI.lua | 116 -- GoonBase/lua/MenuSceneManager.lua | 23 - GoonBase/lua/MenuSetup.lua | 19 - GoonBase/lua/MissionBriefingGUI.lua | 15 - GoonBase/lua/NPCRaycastWeaponBase.lua | 25 - GoonBase/lua/NarrativeTweakData.lua | 13 - GoonBase/lua/NetworkGame.lua | 26 - GoonBase/lua/NetworkMatchMakingSteam.lua | 22 - GoonBase/lua/NewRaycastWeaponBase.lua | 30 - GoonBase/lua/PlayerDamage.lua | 25 - GoonBase/lua/PlayerInventory.lua | 45 - GoonBase/lua/PlayerManager.lua | 22 - GoonBase/lua/PlayerStandard.lua | 34 - GoonBase/lua/PreplanningTweakData.lua | 40 - GoonBase/lua/QuickSmokeGrenade.lua | 19 - GoonBase/lua/Setup.lua | 13 - GoonBase/lua/SpoocLogicAttack.lua | 47 - GoonBase/lua/TweakData.lua | 14 - GoonBase/lua/WeaponFlashlight.lua | 51 - GoonBase/lua/WeaponLaser.lua | 42 - GoonBase/lua/WeaponTweakData.lua | 13 - GoonBase/mods/body_count.lua | 288 --- GoonBase/mods/colors/color_hsvrgb.lua | 219 --- GoonBase/mods/colors/enemy_weapon_laser.lua | 252 --- GoonBase/mods/colors/weapon_flashlight.lua | 210 --- GoonBase/mods/colors/weapon_laser.lua | 397 ---- GoonBase/mods/colors/world_laser_colors.lua | 181 -- GoonBase/mods/colour_grading.lua | 198 -- GoonBase/mods/custom_grenades.lua | 33 - GoonBase/mods/custom_waypoints.lua | 273 --- GoonBase/mods/extended_inventory.lua | 335 ---- GoonBase/mods/gage_coins.lua | 83 - GoonBase/mods/grenade_indicator.lua | 80 - GoonBase/mods/mod_shop.lua | 531 ------ GoonBase/mods/mutators.lua | 900 --------- GoonBase/mods/push_to_interact.lua | 246 --- GoonBase/mods/trading.lua | 1579 ---------------- GoonBase/mods/train_heist_plans.lua | 122 -- GoonBase/mods/weapon_customization.lua | 647 ------- GoonBase/mods/weapon_customization_menus.lua | 795 -------- .../mods/weapon_customization_part_data.lua | 372 ---- GoonBase/mods/weapon_remember_gadget.lua | 45 - GoonBase/mods/zoom_sensitivity.lua | 74 - GoonBase/mutators/base_mutator.lua | 251 --- GoonBase/mutators/mutator_addicts.lua | 122 -- GoonBase/mutators/mutator_all_bulldozers.lua | 162 -- GoonBase/mutators/mutator_all_cloakers.lua | 163 -- GoonBase/mutators/mutator_all_shields.lua | 162 -- GoonBase/mutators/mutator_all_tazers.lua | 162 -- .../mutators/mutator_exploding_bullets.lua | 65 - GoonBase/mutators/mutator_floating_bodies.lua | 81 - .../mutators/mutator_insane_spawnrate.lua | 455 ----- .../mutator_insane_spawnrate_cops.lua | 298 --- GoonBase/mutators/mutator_instagib.lua | 43 - GoonBase/mutators/mutator_jamming_weapons.lua | 42 - GoonBase/mutators/mutator_lightning_speed.lua | 135 -- GoonBase/mutators/mutator_no_ammo_pickups.lua | 74 - GoonBase/mutators/mutator_realism_mode.lua | 56 - GoonBase/mutators/mutator_shielddozers.lua | 61 - .../mutators/mutator_suicidal_spawnrate.lua | 538 ------ .../mutator_suicidal_spawnrate_cops.lua | 378 ---- .../mutators/mutator_suicide_cloakers.lua | 67 - GoonBase/mutators/mutator_unbreakable.lua | 34 - GoonBase/req/SimpleMenu.lua | 131 -- GoonBase/req/autils.lua | 226 --- GoonBase/req/hooks.lua | 237 --- GoonBase/req/hooks_command_queue.lua | 65 - GoonBase/req/localization.lua | 34 - GoonBase/req/menus.lua | 361 ---- GoonBase/req/mods.lua | 412 ----- GoonBase/req/network.lua | 245 --- GoonBase/req/options.lua | 122 -- GoonBase/req/updates.lua | 764 -------- IPHLPAPI.dll | Bin 232448 -> 0 bytes PD2Hook.yml | 17 - update_list.txt | 115 -- update_version.txt | 4 - 118 files changed, 18126 deletions(-) delete mode 100644 GoonBase/goonbase.lua delete mode 100644 GoonBase/lua/AchievementManager.lua delete mode 100644 GoonBase/lua/ActionSpooc.lua delete mode 100644 GoonBase/lua/AssetsTweakData.lua delete mode 100644 GoonBase/lua/BlackMarketGUI.lua delete mode 100644 GoonBase/lua/BlackMarketManager.lua delete mode 100644 GoonBase/lua/BlackMarketTweakData.lua delete mode 100644 GoonBase/lua/CharacterTweakData.lua delete mode 100644 GoonBase/lua/ChatManager.lua delete mode 100644 GoonBase/lua/ContourExt.lua delete mode 100644 GoonBase/lua/CopActionHurt.lua delete mode 100644 GoonBase/lua/CopDamage.lua delete mode 100644 GoonBase/lua/CopInventory.lua delete mode 100644 GoonBase/lua/CoreMenuInput.lua delete mode 100644 GoonBase/lua/CoreMenuItem.lua delete mode 100644 GoonBase/lua/CoreMenuItemSlider.lua delete mode 100644 GoonBase/lua/CoreMenuLogic.lua delete mode 100644 GoonBase/lua/CriminalsManager.lua delete mode 100644 GoonBase/lua/ElementLaserTrigger.lua delete mode 100644 GoonBase/lua/ElementSpawnGrenade.lua delete mode 100644 GoonBase/lua/EnemyManager.lua delete mode 100644 GoonBase/lua/FPCameraPlayerBase.lua delete mode 100644 GoonBase/lua/FragGrenade.lua delete mode 100644 GoonBase/lua/GageAssignmentManager.lua delete mode 100644 GoonBase/lua/GameSetup.lua delete mode 100644 GoonBase/lua/GameStateMachine.lua delete mode 100644 GoonBase/lua/GroupAIManager.lua delete mode 100644 GoonBase/lua/GroupAIStateBase.lua delete mode 100644 GoonBase/lua/GroupAIStateBesiege.lua delete mode 100644 GoonBase/lua/GroupAITweakData.lua delete mode 100644 GoonBase/lua/HUDManager.lua delete mode 100644 GoonBase/lua/HUDManagerPD2.lua delete mode 100644 GoonBase/lua/InfamyTweakData.lua delete mode 100644 GoonBase/lua/InteractionExt.lua delete mode 100644 GoonBase/lua/JobManager.lua delete mode 100644 GoonBase/lua/LevelsTweakData.lua delete mode 100644 GoonBase/lua/LocalizationManager.lua delete mode 100644 GoonBase/lua/MaskExt.lua delete mode 100644 GoonBase/lua/MenuComponentManager.lua delete mode 100644 GoonBase/lua/MenuItemCustomizeController.lua delete mode 100644 GoonBase/lua/MenuManager.lua delete mode 100644 GoonBase/lua/MenuNodeGUI.lua delete mode 100644 GoonBase/lua/MenuSceneManager.lua delete mode 100644 GoonBase/lua/MenuSetup.lua delete mode 100644 GoonBase/lua/MissionBriefingGUI.lua delete mode 100644 GoonBase/lua/NPCRaycastWeaponBase.lua delete mode 100644 GoonBase/lua/NarrativeTweakData.lua delete mode 100644 GoonBase/lua/NetworkGame.lua delete mode 100644 GoonBase/lua/NetworkMatchMakingSteam.lua delete mode 100644 GoonBase/lua/NewRaycastWeaponBase.lua delete mode 100644 GoonBase/lua/PlayerDamage.lua delete mode 100644 GoonBase/lua/PlayerInventory.lua delete mode 100644 GoonBase/lua/PlayerManager.lua delete mode 100644 GoonBase/lua/PlayerStandard.lua delete mode 100644 GoonBase/lua/PreplanningTweakData.lua delete mode 100644 GoonBase/lua/QuickSmokeGrenade.lua delete mode 100644 GoonBase/lua/Setup.lua delete mode 100644 GoonBase/lua/SpoocLogicAttack.lua delete mode 100644 GoonBase/lua/TweakData.lua delete mode 100644 GoonBase/lua/WeaponFlashlight.lua delete mode 100644 GoonBase/lua/WeaponLaser.lua delete mode 100644 GoonBase/lua/WeaponTweakData.lua delete mode 100644 GoonBase/mods/body_count.lua delete mode 100644 GoonBase/mods/colors/color_hsvrgb.lua delete mode 100644 GoonBase/mods/colors/enemy_weapon_laser.lua delete mode 100644 GoonBase/mods/colors/weapon_flashlight.lua delete mode 100644 GoonBase/mods/colors/weapon_laser.lua delete mode 100644 GoonBase/mods/colors/world_laser_colors.lua delete mode 100644 GoonBase/mods/colour_grading.lua delete mode 100644 GoonBase/mods/custom_grenades.lua delete mode 100644 GoonBase/mods/custom_waypoints.lua delete mode 100644 GoonBase/mods/extended_inventory.lua delete mode 100644 GoonBase/mods/gage_coins.lua delete mode 100644 GoonBase/mods/grenade_indicator.lua delete mode 100644 GoonBase/mods/mod_shop.lua delete mode 100644 GoonBase/mods/mutators.lua delete mode 100644 GoonBase/mods/push_to_interact.lua delete mode 100644 GoonBase/mods/trading.lua delete mode 100644 GoonBase/mods/train_heist_plans.lua delete mode 100644 GoonBase/mods/weapon_customization.lua delete mode 100644 GoonBase/mods/weapon_customization_menus.lua delete mode 100644 GoonBase/mods/weapon_customization_part_data.lua delete mode 100644 GoonBase/mods/weapon_remember_gadget.lua delete mode 100644 GoonBase/mods/zoom_sensitivity.lua delete mode 100644 GoonBase/mutators/base_mutator.lua delete mode 100644 GoonBase/mutators/mutator_addicts.lua delete mode 100644 GoonBase/mutators/mutator_all_bulldozers.lua delete mode 100644 GoonBase/mutators/mutator_all_cloakers.lua delete mode 100644 GoonBase/mutators/mutator_all_shields.lua delete mode 100644 GoonBase/mutators/mutator_all_tazers.lua delete mode 100644 GoonBase/mutators/mutator_exploding_bullets.lua delete mode 100644 GoonBase/mutators/mutator_floating_bodies.lua delete mode 100644 GoonBase/mutators/mutator_insane_spawnrate.lua delete mode 100644 GoonBase/mutators/mutator_insane_spawnrate_cops.lua delete mode 100644 GoonBase/mutators/mutator_instagib.lua delete mode 100644 GoonBase/mutators/mutator_jamming_weapons.lua delete mode 100644 GoonBase/mutators/mutator_lightning_speed.lua delete mode 100644 GoonBase/mutators/mutator_no_ammo_pickups.lua delete mode 100644 GoonBase/mutators/mutator_realism_mode.lua delete mode 100644 GoonBase/mutators/mutator_shielddozers.lua delete mode 100644 GoonBase/mutators/mutator_suicidal_spawnrate.lua delete mode 100644 GoonBase/mutators/mutator_suicidal_spawnrate_cops.lua delete mode 100644 GoonBase/mutators/mutator_suicide_cloakers.lua delete mode 100644 GoonBase/mutators/mutator_unbreakable.lua delete mode 100644 GoonBase/req/SimpleMenu.lua delete mode 100644 GoonBase/req/autils.lua delete mode 100644 GoonBase/req/hooks.lua delete mode 100644 GoonBase/req/hooks_command_queue.lua delete mode 100644 GoonBase/req/localization.lua delete mode 100644 GoonBase/req/menus.lua delete mode 100644 GoonBase/req/mods.lua delete mode 100644 GoonBase/req/network.lua delete mode 100644 GoonBase/req/options.lua delete mode 100644 GoonBase/req/updates.lua delete mode 100644 IPHLPAPI.dll delete mode 100644 PD2Hook.yml delete mode 100644 update_list.txt delete mode 100644 update_version.txt diff --git a/GoonBase/goonbase.lua b/GoonBase/goonbase.lua deleted file mode 100644 index ef3cc02..0000000 --- a/GoonBase/goonbase.lua +++ /dev/null @@ -1,246 +0,0 @@ ----------- --- Payday 2 GoonMod, Public Release Beta 2, built on 1/23/2015 10:01:12 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -if not _G.GoonBase then - _G.GoonBase = {} - GoonBase.Version = 27 - GoonBase.GameVersion = "1.24.2" - GoonBase.LogFile = "GoonBase.log" - GoonBase.Path = "GoonBase/" - GoonBase.LuaPath = "GoonBase/lua/" - GoonBase.SafeMode = true -end - -GoonBase.RequireScripts = { - "req/autils.lua", - "req/hooks.lua", - "req/hooks_command_queue.lua", - "req/localization.lua", - "req/menus.lua", - "req/mods.lua", - "req/network.lua", - "req/options.lua", - "req/SimpleMenu.lua", - "req/updates.lua", -} - -GoonBase.ModFiles = { - "mods/colors/color_hsvrgb.lua", - "mods/colors/enemy_weapon_laser.lua", - "mods/colors/weapon_flashlight.lua", - "mods/colors/weapon_laser.lua", - "mods/colors/world_laser_colors.lua", - "mods/body_count.lua", - "mods/colour_grading.lua", - "mods/custom_grenades.lua", - "mods/custom_waypoints.lua", - "mods/extended_inventory.lua", - "mods/gage_coins.lua", - "mods/grenade_indicator.lua", - "mods/mod_shop.lua", - "mods/mutators.lua", - "mods/push_to_interact.lua", - "mods/trading.lua", - "mods/train_heist_plans.lua", - "mods/weapon_customization.lua", - "mods/weapon_customization_menus.lua", - "mods/weapon_customization_part_data.lua", - "mods/weapon_remember_gadget.lua", - "mods/zoom_sensitivity.lua", -} - -GoonBase.RequireHookFiles = { - "lib/managers/localizationmanager", - "lib/managers/menumanager", - "lib/setups/menusetup" -} - -GoonBase.HookFiles = { - - ["lib/managers/localizationmanager"] = "LocalizationManager.lua", - ["lib/managers/menumanager"] = "MenuManager.lua", - ["lib/managers/chatmanager"] = "ChatManager.lua", - ["lib/managers/enemymanager"] = "EnemyManager.lua", - ["lib/units/weapons/grenades/quicksmokegrenade"] = "QuickSmokeGrenade.lua", - ["lib/managers/hudmanager"] = "HUDManager.lua", - ["lib/managers/jobmanager"] = "JobManager.lua", - ["lib/managers/groupaimanager"] = "GroupAIManager.lua", - ["lib/managers/group_ai_states/groupaistatebase"] = "GroupAIStateBase.lua", - ["lib/managers/group_ai_states/groupaistatebesiege"] = "GroupAIStateBesiege.lua", - ["lib/units/beings/player/states/playerstandard"] = "PlayerStandard.lua", - ["lib/units/beings/player/playerdamage"] = "PlayerDamage.lua", - ["lib/managers/playermanager"] = "PlayerManager.lua", - ["lib/managers/gageassignmentmanager"] = "GageAssignmentManager.lua", - ["lib/managers/achievmentmanager"] = "AchievementManager.lua", - ["lib/tweak_data/infamytweakdata"] = "InfamyTweakData.lua", - -- ["lib/setups/setup"] = "Setup.lua", - ["lib/setups/gamesetup"] = "GameSetup.lua", - ["lib/setups/menusetup"] = "MenuSetup.lua", - ["lib/managers/menu/blackmarketgui"] = "BlackMarketGUI.lua", - ["lib/managers/blackmarketmanager"] = "BlackMarketManager.lua", - ["lib/tweak_data/groupaitweakdata"] = "GroupAITweakData.lua", - ["lib/tweak_data/charactertweakdata"] = "CharacterTweakData.lua", - ["lib/units/enemies/cop/copinventory"] = "CopInventory.lua", - ["lib/units/enemies/cop/copdamage"] = "CopDamage.lua", - ["lib/managers/mission/elementlasertrigger"] = "ElementLaserTrigger.lua", - ["lib/units/weapons/weaponflashlight"] = "WeaponFlashlight.lua", - ["lib/units/weapons/weaponlaser"] = "WeaponLaser.lua", - ["lib/tweak_data/levelstweakdata"] = "LevelsTweakData.lua", - ["lib/tweak_data/assetstweakdata"] = "AssetsTweakData.lua", - ["lib/tweak_data/narrativetweakdata"] = "NarrativeTweakData.lua", - ["lib/managers/menu/menunodegui"] = "MenuNodeGUI.lua", - ["lib/managers/menu/items/menuitemcustomizecontroller"] = "MenuItemCustomizeController.lua", - -- ["lib/network/networkgame"] = "NetworkGame.lua", - ["lib/managers/criminalsmanager"] = "CriminalsManager.lua", - ["lib/units/weapons/newraycastweaponbase"] = "NewRaycastWeaponBase.lua", - ["lib/units/weapons/npcraycastweaponbase"] = "NPCRaycastWeaponBase.lua", - -- ["lib/units/beings/player/playerinventory"] = "PlayerInventory.lua", - ["lib/units/cameras/fpcameraplayerbase"] = "FPCameraPlayerBase.lua", - ["core/lib/managers/menu/items/coremenuitemslider"] = "CoreMenuItemSlider.lua", - ["lib/utils/game_state_machine/gamestatemachine"] = "GameStateMachine.lua", - ["lib/units/contourext"] = "ContourExt.lua", - ["lib/units/interactions/interactionext"] = "InteractionExt.lua", - ["lib/units/enemies/spooc/actions/lower_body/actionspooc"] = "ActionSpooc.lua", - ["lib/managers/menu/menucomponentmanager"] = "MenuComponentManager.lua", - ["lib/managers/menu/missionbriefinggui"] = "MissionBriefingGUI.lua", - ["lib/network/matchmaking/networkmatchmakingsteam"] = "NetworkMatchMakingSteam.lua", - -- ["lib/units/maskext"] = "MaskExt.lua", - ["lib/managers/menu/menuscenemanager"] = "MenuSceneManager.lua", - - ["lib/units/enemies/cop/actions/full_body/copactionhurt"] = "CopActionHurt.lua", - -} - --- Required Global Functions -function _G.Print( ... ) - - local str = "" - for k, v in ipairs( arg ) do - str = str .. tostring(v) - end - str = str .. "\n" - io.stderr:write( str ) - - local file = io.open( GoonBase.LogFile, "a+" ) - if file ~= nil then - - io.output( file ) - - if GoonBase._print_cache ~= nil then - for k, v in ipairs(GoonBase._print_cache) do - io.write( v ) - end - GoonBase._print_cache = {} - end - io.write( str ) - - io.close( file ) - - else - - io.stderr:write( "[Error] Could not write to file, caching print string: '" .. str .. "'" ) - if GoonBase._print_cache == nil then - GoonBase._print_cache = {} - end - table.insert( GoonBase._print_cache, str ) - - end - -end - -function io.file_is_readable( fname ) - local file = io.open(fname, "r" ) - if file ~= nil then - io.close(file) - return true - end - return false -end - -function _G.SafeDoFile( fileName ) - - local success, errorMsg = pcall(function() - if io.file_is_readable( fileName ) then - dofile( fileName ) - else - Print("[Error] Could not open file '" .. fileName .. "'! Does it exist, is it readable?") - end - end) - - if not success then - Print("[Error]\nFile: " .. fileName .. "\n" .. errorMsg) - end - -end - -local unsupported = true - --- Load Require and Mod Scripts -if not GoonBase.HasLoadedScripts then - - -- Check required classes exist now - if class and Application and string.split then - - GoonBase.HasLoadedScripts = true - - -- Load required files - for k, v in pairs( GoonBase.RequireScripts ) do - SafeDoFile( GoonBase.Path .. v ) - end - - -- Check if version is supported - if GoonBase.Updates ~= nil then - GoonBase.SupportedVersion = GoonBase.Updates:IsSupportedVersion() - end - - -- Run hooks - if Hooks ~= nil then - - Hooks:RegisterHook("GoonBasePostLoadMods") - Hooks:Call("GoonBasePostLoadMods") - - -- Load default options - local Options = GoonBase.Options - if Options:UsingDefaults() then - Options:LoadDefaults() - end - - Hooks:RegisterHook("GoonBasePostLoadedMods") - Hooks:Call("GoonBasePostLoadedMods") - - end - - end - -end - --- Load Hook Scripts -if not _G.RegisteredScripts and RegisterScript then - RegisterScript("GoonBase/goonbase.lua", 2, "*") - _G.RegisteredScripts = true -end - -if RequiredScript then - - local requiredScript = RequiredScript:lower() - if GoonBase.HookFiles[requiredScript] then - - - if GoonBase.SupportedVersion or (not GoonBase.SupportedVersion and table.contains(GoonBase.RequireHookFiles, requiredScript)) then - - if type( GoonBase.HookFiles[requiredScript] ) == "table" then - for k, v in pairs( GoonBase.HookFiles[requiredScript] ) do - SafeDoFile( GoonBase.LuaPath .. v ) - end - else - SafeDoFile( GoonBase.LuaPath .. GoonBase.HookFiles[requiredScript] ) - end - - end - - end - -end --- END OF FILE diff --git a/GoonBase/lua/AchievementManager.lua b/GoonBase/lua/AchievementManager.lua deleted file mode 100644 index 49eefc7..0000000 --- a/GoonBase/lua/AchievementManager.lua +++ /dev/null @@ -1,61 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( AchievmentManager ) - -AchievmentManager._disabled_stack = {} - -Hooks:RegisterHook("AchievementManagerPreAward") -function AchievmentManager.award(self, id) - - Hooks:Call("AchievementManagerPreAward", self, id) - - AchievmentManager:CheckAchievementsDisabled() - if self:AchievementsDisabled() then - return - end - - self.orig.award(self, id) - -end - -Hooks:RegisterHook("AchievementManagerPreAwardProgress") -function AchievmentManager.award_progress(self, stat, value) - - Hooks:Call("AchievementManagerPreAwardProgress", self, stat, value) - - AchievmentManager:CheckAchievementsDisabled() - if self:AchievementsDisabled() then - return - end - - self.orig.award_progress(self, stat, value) - -end - -function AchievmentManager:AchievementsDisabled() - local disabled = false - for k, v in pairs( self._disabled_stack ) do - if v ~= nil and v == true then - disabled = true - end - end - return disabled -end - -function AchievmentManager:DisableAchievements(id) - self._disabled_stack[id] = true -end - -function AchievmentManager:EnableAchievements(id) - self._disabled_stack[id] = nil -end - -Hooks:RegisterHook("AchievementManagerCheckDisable") -function AchievmentManager:CheckAchievementsDisabled() - AchievmentManager._disabled_stack = {} - Hooks:Call("AchievementManagerCheckDisable", self) -end --- END OF FILE diff --git a/GoonBase/lua/ActionSpooc.lua b/GoonBase/lua/ActionSpooc.lua deleted file mode 100644 index 19f0d66..0000000 --- a/GoonBase/lua/ActionSpooc.lua +++ /dev/null @@ -1,19 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( ActionSpooc ) - -Hooks:RegisterHook("ActionSpoocInitialize") -function ActionSpooc.init(self, action_desc, common_data) - Hooks:Call("ActionSpoocInitialize", self, action_desc, common_data) - return self.orig.init(self, action_desc, common_data) -end - -Hooks:RegisterHook("ActionSpoocAnimActCallback") -function ActionSpooc.anim_act_clbk(self, anim_act) - Hooks:Call("ActionSpoocAnimActCallback", self, anim_act) - return self.orig.anim_act_clbk(self, anim_act) -end --- END OF FILE diff --git a/GoonBase/lua/AssetsTweakData.lua b/GoonBase/lua/AssetsTweakData.lua deleted file mode 100644 index f1313ce..0000000 --- a/GoonBase/lua/AssetsTweakData.lua +++ /dev/null @@ -1,20 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( AssetsTweakData ) - -function AssetsTweakData._init_assets(self, tweak_data) - - self.orig._init_assets(self, tweak_data) - - table.insert(self.bodybags_bag.stages, "arm_for_prof") - table.insert(self.grenade_crate.stages, "arm_for_prof") - - table.insert(self.arm_for_info.stages, "arm_for_prof") - table.insert(self.arm_for_ammo.stages, "arm_for_prof") - table.insert(self.arm_for_health.stages, "arm_for_prof") - -end --- END OF FILE diff --git a/GoonBase/lua/BlackMarketGUI.lua b/GoonBase/lua/BlackMarketGUI.lua deleted file mode 100644 index e662aa5..0000000 --- a/GoonBase/lua/BlackMarketGUI.lua +++ /dev/null @@ -1,1637 +0,0 @@ ----------- --- Payday 2 GoonMod, Public Release Beta 2, built on 1/9/2015 9:30:33 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( BlackMarketGui ) - -local is_win32 = SystemInfo:platform() == Idstring("WIN32") -local NOT_WIN_32 = not is_win32 -local WIDTH_MULTIPLIER = NOT_WIN_32 and 0.68 or 0.71 -local BOX_GAP = 13.5 -local GRID_H_MUL = (NOT_WIN_32 and 7 or 6.6) / 8 -local massive_font = tweak_data.menu.pd2_massive_font -local large_font = tweak_data.menu.pd2_large_font -local medium_font = tweak_data.menu.pd2_medium_font -local small_font = tweak_data.menu.pd2_small_font -local massive_font_size = tweak_data.menu.pd2_massive_font_size -local large_font_size = tweak_data.menu.pd2_large_font_size -local medium_font_size = tweak_data.menu.pd2_medium_font_size -local small_font_size = tweak_data.menu.pd2_small_font_size - -local Localization = GoonBase.Localization -Localization.bm_menu_amount_locked = "NONE IN STOCK" - -function BlackMarketGui.init(self, ws, fullscreen_ws, node) - - self._ws = ws - self._fullscreen_ws = fullscreen_ws - self._init_layer = self._ws:panel():layer() - self._node = node - local component_data = self._node:parameters().menu_component_data - - -- Fix for custom tabs crashing inventory - if node._parameters.name == "blackmarket" then - component_data = nil - end - - local do_animation = not component_data and not self._data - local is_start_page = not component_data and true or false - - managers.menu_component:close_contract_gui() - self:_setup(is_start_page, component_data) - if do_animation then - local fade_me_in_scotty = function(o) - over(0.1, function(p) - o:set_alpha(p) - end -) - end - - self._panel:animate(fade_me_in_scotty) - self._fullscreen_panel:animate(fade_me_in_scotty) - end - self:set_enabled(true) - -end - -Hooks:RegisterHook("BlackMarketGUIPreSetup") -Hooks:RegisterHook("BlackMarketGUIPostSetup") -function BlackMarketGui._setup(self, is_start_page, component_data) - - local psuccess, perror = pcall(function() - - Hooks:Call("BlackMarketGUIPreSetup", self, is_start_page, component_data) - self.orig._setup(self, is_start_page, component_data) - Hooks:Call("BlackMarketGUIPostSetup", self, is_start_page, component_data) - self:on_slot_selected( self._selected_slot ) - - end) - if not psuccess then - Print("[Error] " .. perror) - end - -end - -Hooks:RegisterHook("BlackMarketGUIOnPopulateWeapons") -Hooks:RegisterHook("BlackMarketGUIOnPopulateWeaponActionList") -function BlackMarketGui.populate_weapon_category(self, category, data) - - Hooks:Call("BlackMarketGUIOnPopulateWeapons", self, category, data) - - local crafted_category = managers.blackmarket:get_crafted_category(category) or {} - local last_weapon = table.size(crafted_category) == 1 - local last_unlocked_weapon - if not last_weapon then - - local category_size = table.size(crafted_category) - for i, crafted in pairs(crafted_category) do - if not managers.blackmarket:weapon_unlocked(crafted.weapon_id) then - category_size = category_size - 1 - end - end - - last_unlocked_weapon = category_size == 1 - - end - - local hold_crafted_item = managers.blackmarket:get_hold_crafted_item() - local currently_holding = hold_crafted_item and hold_crafted_item.category == category - local max_items = data.override_slots and data.override_slots[1] * data.override_slots[2] or 9 - local max_rows = tweak_data.gui.MAX_WEAPON_ROWS or 3 - - max_items = max_rows * (data.override_slots and data.override_slots[2] or 3) - for i = 1, max_items do - data[i] = nil - end - - local guis_catalog = "guis/" - local weapon_data = Global.blackmarket_manager.weapons - local new_data = {} - local index = 0 - - for i, crafted in pairs(crafted_category) do - - guis_catalog = "guis/" - local bundle_folder = tweak_data.weapon[crafted.weapon_id] and tweak_data.weapon[crafted.weapon_id].texture_bundle_folder - if bundle_folder then - guis_catalog = guis_catalog .. "dlcs/" .. tostring(bundle_folder) .. "/" - end - - new_data = {} - new_data.name = crafted.weapon_id - new_data.name_localized = managers.blackmarket:get_weapon_name_by_category_slot(category, i) - new_data.raw_name_localized = managers.weapon_factory:get_weapon_name_by_factory_id(crafted.factory_id) - new_data.custom_name_text = managers.blackmarket:get_crafted_custom_name(category, i, true) - new_data.category = category - new_data.slot = i - new_data.unlocked = managers.blackmarket:weapon_unlocked(crafted.weapon_id) - new_data.level = managers.blackmarket:weapon_level(crafted.weapon_id) - new_data.can_afford = true - new_data.equipped = crafted.equipped - new_data.skill_based = weapon_data[crafted.weapon_id].skill_based - new_data.skill_name = new_data.skill_based and "bm_menu_skill_locked_" .. new_data.name - new_data.price = managers.money:get_weapon_slot_sell_value(category, i) - local texture_name = tweak_data.weapon[crafted.weapon_id].texture_name or tostring(crafted.weapon_id) - new_data.bitmap_texture = guis_catalog .. "textures/pd2/blackmarket/icons/weapons/" .. texture_name - new_data.comparision_data = new_data.unlocked and managers.blackmarket:get_weapon_stats(category, i) - new_data.global_value = tweak_data.weapon[new_data.name] and tweak_data.weapon[new_data.name].global_value or "normal" - new_data.dlc_locked = tweak_data.lootdrop.global_values[new_data.global_value].unlock_id or nil - new_data.lock_texture = self:get_lock_icon(new_data) - new_data.holding = currently_holding and hold_crafted_item.slot == i - - local icon_list = managers.menu_component:create_weapon_mod_icon_list(crafted.weapon_id, category, crafted.factory_id, i) - local icon_index = 1 - local new_parts = {} - for k, part in pairs(managers.blackmarket:get_weapon_new_part_drops(crafted.factory_id) or {}) do - local type = tweak_data.weapon.factory.parts[part].type - new_parts[type] = true - end - - if table.size(new_parts) > 0 then - new_data.new_drop_data = {} - end - - new_data.mini_icons = {} - for k, icon in pairs(icon_list) do - table.insert(new_data.mini_icons, { - texture = icon.texture, - right = (icon_index - 1) * 18, - bottom = 0, - layer = 1, - w = 16, - h = 16, - stream = false, - alpha = icon.equipped and 1 or 0.25 - }) - if new_parts[icon.type] then - table.insert(new_data.mini_icons, { - texture = "guis/textures/pd2/blackmarket/inv_mod_new", - right = (icon_index - 1) * 18, - bottom = 16, - layer = 1, - w = 16, - h = 8, - stream = false, - alpha = 1 - }) - end - - icon_index = icon_index + 1 - end - - if not new_data.unlocked then - new_data.last_weapon = last_weapon - else - new_data.last_weapon = last_weapon or last_unlocked_weapon - end - - if new_data.equipped then - self._equipped_comparision_data = self._equipped_comparision_data or {} - self._equipped_comparision_data[category] = new_data.comparision_data - end - - if currently_holding then - new_data.selected_text = managers.localization:to_upper_text("bm_menu_btn_swap_weapon") - if new_data.slot ~= hold_crafted_item.slot then - table.insert(new_data, "w_swap") - end - - table.insert(new_data, "i_stop_move") - else - local has_mods = managers.weapon_factory:has_weapon_more_than_default_parts(crafted.factory_id) - if has_mods and new_data.unlocked then - table.insert(new_data, "w_mod") - end - - if not new_data.last_weapon then - table.insert(new_data, "w_sell") - end - - if not new_data.equipped and new_data.unlocked then - table.insert(new_data, "w_equip") - end - - if new_data.equipped and new_data.unlocked then - table.insert(new_data, "w_move") - end - - table.insert(new_data, "w_preview") - - Hooks:Call("BlackMarketGUIOnPopulateWeaponActionList", self, new_data) - - end - - data[i] = new_data - index = i - end - - for i = 1, max_items do - if not data[i] then - local can_buy_weapon = managers.blackmarket:is_weapon_slot_unlocked(category, i) - new_data = {} - if can_buy_weapon then - - new_data.name = "bm_menu_btn_buy_new_weapon" - new_data.name_localized = managers.localization:text("bm_menu_empty_weapon_slot") - new_data.mid_text = {} - new_data.mid_text.noselected_text = new_data.name_localized - new_data.mid_text.noselected_color = tweak_data.screen_colors.button_stage_3 - - if not currently_holding or not new_data.mid_text.noselected_text then - end - - new_data.mid_text.selected_text = managers.localization:text("bm_menu_btn_buy_new_weapon") - new_data.mid_text.selected_color = currently_holding and new_data.mid_text.noselected_color or tweak_data.screen_colors.button_stage_2 - new_data.empty_slot = true - new_data.category = category - new_data.slot = i - new_data.unlocked = true - new_data.can_afford = true - new_data.equipped = false - - if currently_holding then - new_data.selected_text = managers.localization:to_upper_text("bm_menu_btn_place_weapon") - table.insert(new_data, "w_place") - table.insert(new_data, "i_stop_move") - else - table.insert(new_data, "ew_buy") - end - - if managers.blackmarket:got_new_drop(new_data.category, "weapon_buy_empty", nil) then - new_data.mini_icons = new_data.mini_icons or {} - table.insert(new_data.mini_icons, { - name = "new_drop", - texture = "guis/textures/pd2/blackmarket/inv_newdrop", - right = 0, - top = 0, - layer = 1, - w = 16, - h = 16, - stream = false, - visible = false - }) - new_data.new_drop_data = {} - end - - else - - new_data.name = "bm_menu_btn_buy_weapon_slot" - new_data.name_localized = managers.localization:text("bm_menu_locked_weapon_slot") - new_data.empty_slot = true - new_data.category = category - new_data.slot = i - new_data.unlocked = true - new_data.equipped = false - new_data.lock_texture = "guis/textures/pd2/blackmarket/money_lock" - new_data.lock_color = tweak_data.screen_colors.button_stage_3 - new_data.lock_shape = { - w = 32, - h = 32, - x = 0, - y = -32 - } - new_data.locked_slot = true - new_data.dlc_locked = managers.experience:cash_string(managers.money:get_buy_weapon_slot_price()) - new_data.mid_text = {} - new_data.mid_text.noselected_text = new_data.name_localized - new_data.mid_text.noselected_color = tweak_data.screen_colors.button_stage_3 - new_data.mid_text.is_lock_same_color = true - - if currently_holding then - new_data.mid_text.selected_text = new_data.mid_text.noselected_text - new_data.mid_text.selected_color = new_data.mid_text.noselected_color - table.insert(new_data, "i_stop_move") - elseif managers.money:can_afford_buy_weapon_slot() then - new_data.mid_text.selected_text = managers.localization:text("bm_menu_btn_buy_weapon_slot") - new_data.mid_text.selected_color = tweak_data.screen_colors.button_stage_2 - table.insert(new_data, "ew_unlock") - else - new_data.mid_text.selected_text = managers.localization:text("bm_menu_cannot_buy_weapon_slot") - new_data.mid_text.selected_color = tweak_data.screen_colors.important_1 - new_data.dlc_locked = new_data.dlc_locked .. " " .. managers.localization:to_upper_text("bm_menu_cannot_buy_weapon_slot") - new_data.mid_text.lock_noselected_color = tweak_data.screen_colors.important_1 - new_data.cannot_buy = true - end - - end - - data[i] = new_data - end - - end - -end - -Hooks:RegisterHook("BlackMarketGUIOnPopulateMeleeWeapons") -Hooks:RegisterHook("BlackMarketGUIOnPopulateMeleeWeaponActionList") -function BlackMarketGui.populate_melee_weapons(self, data) - - Hooks:Call("BlackMarketGUIOnPopulateMeleeWeapons", self, data) - - local new_data = {} - local sort_data = {} - local xd, yd, x_td, y_td, x_sn, y_sn, x_gv, y_gv - local m_tweak_data = tweak_data.blackmarket.melee_weapons - local l_tweak_data = tweak_data.lootdrop.global_values - local global_value - - for id, d in pairs(Global.blackmarket_manager.melee_weapons) do - global_value = tweak_data.blackmarket.melee_weapons[id].dlc or tweak_data.blackmarket.melee_weapons[id].global_value or "normal" - if d.unlocked or d.equipped or not tweak_data:get_raw_value("lootdrop", "global_values", global_value, "hide_unavailable") then - table.insert(sort_data, {id, d}) - end - end - - table.sort(sort_data, function(x, y) - - xd = x[2] - yd = y[2] - x_td = m_tweak_data[x[1]] - y_td = m_tweak_data[y[1]] - - if not xd.is_favorite ~= not yd.is_favorite then - return xd.is_favorite - end - if xd.unlocked ~= yd.unlocked then - return xd.unlocked - end - if x_td.instant ~= y_td.instant then - return x_td.instant - end - if xd.skill_based ~= yd.skill_based then - return xd.skill_based - end - if x_td.free ~= y_td.free then - return x_td.free - end - - x_gv = x_td.global_value or x_td.dlc or "normal" - y_gv = y_td.global_value or y_td.dlc or "normal" - x_sn = l_tweak_data[x_gv] - y_sn = l_tweak_data[y_gv] - x_sn = x_sn and x_sn.sort_number or 1 - y_sn = y_sn and y_sn.sort_number or 1 - - if x_sn ~= y_sn then - return x_sn < y_sn - end - if xd.level ~= yd.level then - return xd.level < yd.level - end - - return x[1] < y[1] - - end) - - local max_items = math.ceil(#sort_data / (data.override_slots[1] or 3)) * (data.override_slots[1] or 3) - local index = 0 - local guis_catalog, m_tweak_data, melee_weapon_id - - for i = 1, max_items do - data[i] = nil - end - - for i, melee_weapon_data in ipairs(sort_data) do - - melee_weapon_id = melee_weapon_data[1] - m_tweak_data = tweak_data.blackmarket.melee_weapons[melee_weapon_data[1]] or {} - guis_catalog = "guis/" - - local bundle_folder = m_tweak_data.texture_bundle_folder - if bundle_folder then - guis_catalog = guis_catalog .. "dlcs/" .. tostring(bundle_folder) .. "/" - end - - new_data = {} - new_data.name = melee_weapon_id - new_data.name_localized = managers.localization:text(tweak_data.blackmarket.melee_weapons[new_data.name].name_id) - new_data.category = "melee_weapons" - new_data.slot = i - new_data.unlocked = melee_weapon_data[2].unlocked - new_data.equipped = melee_weapon_data[2].equipped - new_data.level = melee_weapon_data[2].level - new_data.stream = true - new_data.global_value = m_tweak_data.dlc or "normal" - new_data.skill_based = melee_weapon_data[2].skill_based - new_data.skill_name = "bm_menu_skill_locked_" .. new_data.name - - if m_tweak_data and m_tweak_data.locks then - - local dlc = m_tweak_data.locks.dlc - local achievement = m_tweak_data.locks.achievement - local saved_job_value = m_tweak_data.locks.saved_job_value - local level = m_tweak_data.locks.level - new_data.dlc_based = true - new_data.lock_texture = self:get_lock_icon(new_data, "guis/textures/pd2/lock_community") - if achievement and managers.achievment:get_info(achievement) and not managers.achievment:get_info(achievement).awarded then - new_data.dlc_locked = "menu_bm_achievement_locked_" .. tostring(achievement) - elseif dlc and not managers.dlc:is_dlc_unlocked(dlc) then - new_data.dlc_locked = tweak_data.lootdrop.global_values[dlc] and tweak_data.lootdrop.global_values[dlc].unlock_id or "bm_menu_dlc_locked" - else - new_data.dlc_locked = tweak_data.lootdrop.global_values[new_data.global_value].unlock_id or "bm_menu_dlc_locked" - end - - else - new_data.lock_texture = self:get_lock_icon(new_data) - new_data.dlc_locked = tweak_data.lootdrop.global_values[new_data.global_value].unlock_id or "bm_menu_dlc_locked" - end - - new_data.bitmap_texture = guis_catalog .. "textures/pd2/blackmarket/icons/melee_weapons/" .. tostring(new_data.name) - - if melee_weapon_id == "weapon" then - new_data.extra_bitmaps = {} - new_data.extra_bitmaps_shape = {} - local primary = managers.blackmarket:equipped_primary() - local primary_id = primary.weapon_id - guis_catalog = "guis/" - local bundle_folder = tweak_data.weapon[primary_id] and tweak_data.weapon[primary_id].texture_bundle_folder - if bundle_folder then - guis_catalog = guis_catalog .. "dlcs/" .. tostring(bundle_folder) .. "/" - end - table.insert(new_data.extra_bitmaps, guis_catalog .. "textures/pd2/blackmarket/icons/weapons/" .. tostring(primary_id)) - table.insert(new_data.extra_bitmaps_shape, { - x = 0, - y = -0.1, - w = 0.75, - h = 0.75 - }) - local secondary = managers.blackmarket:equipped_secondary() - local secondary_id = secondary.weapon_id - guis_catalog = "guis/" - local bundle_folder = tweak_data.weapon[secondary_id] and tweak_data.weapon[secondary_id].texture_bundle_folder - if bundle_folder then - guis_catalog = guis_catalog .. "dlcs/" .. tostring(bundle_folder) .. "/" - end - table.insert(new_data.extra_bitmaps, guis_catalog .. "textures/pd2/blackmarket/icons/weapons/" .. tostring(secondary_id)) - table.insert(new_data.extra_bitmaps_shape, { - x = 0, - y = 0.1, - w = 0.75, - h = 0.75 - }) - end - - if managers.blackmarket:got_new_drop("normal", "melee_weapons", melee_weapon_id) then - new_data.mini_icons = new_data.mini_icons or {} - table.insert(new_data.mini_icons, { - name = "new_drop", - texture = "guis/textures/pd2/blackmarket/inv_newdrop", - right = 0, - top = 0, - layer = 1, - w = 16, - h = 16, - stream = false - }) - new_data.new_drop_data = { - "normal", - "melee_weapons", - melee_weapon_id - } - end - - if new_data.unlocked then - new_data.comparision_data = managers.blackmarket:get_melee_weapon_stats(melee_weapon_id) - end - if new_data.unlocked and not new_data.equipped then - table.insert(new_data, "lo_mw_equip") - end - if new_data.unlocked and data.allow_preview and m_tweak_data.unit then - table.insert(new_data, "lo_mw_preview") - end - - Hooks:Call("BlackMarketGUIOnPopulateMeleeWeaponActionList", self, new_data) - - data[i] = new_data - index = i - - end - - for i = 1, max_items do - if not data[i] then - new_data = {} - new_data.name = "empty" - new_data.name_localized = "" - new_data.category = "melee_weapons" - new_data.slot = i - new_data.unlocked = true - new_data.equipped = false - data[i] = new_data - end - end - -end - -Hooks:RegisterHook("BlackMarketGUIOnPopulateMasks") -Hooks:RegisterHook("BlackMarketGUIOnPopulateMasksActionList") -function BlackMarketGui.populate_masks(self, data) - - local success, err = pcall(function() - - Hooks:Call("BlackMarketGUIOnPopulateMasks", self, data) - - local NOT_WIN_32 = SystemInfo:platform() ~= Idstring("WIN32") - local GRID_H_MUL = (NOT_WIN_32 and 7 or 6.6) / 8 - - local new_data = {} - local crafted_category = managers.blackmarket:get_crafted_category("masks") or {} - local mini_icon_helper = math.round((self._panel:h() - (tweak_data.menu.pd2_medium_font_size + 10) - 60) * GRID_H_MUL / 3) - 16 - local max_items = data.override_slots and data.override_slots[1] * data.override_slots[2] or 9 - local start_crafted_item = data.start_crafted_item or 1 - local hold_crafted_item = managers.blackmarket:get_hold_crafted_item() - local currently_holding = hold_crafted_item and hold_crafted_item.category == "masks" - local max_rows = tweak_data.gui.MAX_MASK_ROWS - max_items = max_rows * (data.override_slots and data.override_slots[2] or 3) - for i = 1, max_items do - data[i] = nil - end - - local guis_catalog = "guis/" - local index = 0 - for i, crafted in pairs(crafted_category) do - - index = i - start_crafted_item + 1 - guis_catalog = "guis/" - local bundle_folder = tweak_data.blackmarket.masks[crafted.mask_id] and tweak_data.blackmarket.masks[crafted.mask_id].texture_bundle_folder - if bundle_folder then - guis_catalog = guis_catalog .. "dlcs/" .. tostring(bundle_folder) .. "/" - end - - new_data = {} - new_data.name = crafted.mask_id - new_data.name_localized = managers.blackmarket:get_mask_name_by_category_slot("masks", i) - new_data.raw_name_localized = managers.localization:text(tweak_data.blackmarket.masks[new_data.name].name_id) - new_data.custom_name_text = managers.blackmarket:get_crafted_custom_name("masks", i, true) - new_data.custom_name_text_right = crafted.modded and -55 or -20 - new_data.custom_name_text_width = crafted.modded and 0.6 - new_data.category = "masks" - new_data.global_value = crafted.global_value - new_data.slot = i - new_data.unlocked = true - new_data.equipped = crafted.equipped - new_data.bitmap_texture = guis_catalog .. "textures/pd2/blackmarket/icons/masks/" .. new_data.name - new_data.stream = false - new_data.holding = currently_holding and hold_crafted_item.slot == i - local is_locked = tweak_data.lootdrop.global_values[new_data.global_value] and tweak_data.lootdrop.global_values[new_data.global_value].dlc and not managers.dlc:has_dlc(new_data.global_value) - local locked_parts = {} - if not is_locked then - for part, type in pairs(crafted.blueprint) do - if tweak_data.lootdrop.global_values[part.global_value] and tweak_data.lootdrop.global_values[part.global_value].dlc and not tweak_data.dlc[part.global_value].free and not managers.dlc:has_dlc(part.global_value) then - locked_parts[type] = part.global_value - is_locked = true - end - end - end - - if is_locked then - new_data.unlocked = false - new_data.lock_texture = self:get_lock_icon(new_data, "guis/textures/pd2/lock_incompatible") - new_data.dlc_locked = tweak_data.lootdrop.global_values[new_data.global_value].unlock_id or "bm_menu_dlc_locked" - end - - if currently_holding then - if i ~= 1 then - new_data.selected_text = managers.localization:to_upper_text("bm_menu_btn_swap_mask") - end - - if i ~= 1 and new_data.slot ~= hold_crafted_item.slot then - table.insert(new_data, "m_swap") - end - - table.insert(new_data, "i_stop_move") - else - if new_data.unlocked then - if not new_data.equipped then - table.insert(new_data, "m_equip") - end - - if i ~= 1 and new_data.equipped then - table.insert(new_data, "m_move") - end - - if not crafted.modded and managers.blackmarket:can_modify_mask(i) and i ~= 1 then - table.insert(new_data, "m_mod") - end - - if i ~= 1 then - table.insert(new_data, "m_preview") - end - - end - - if i ~= 1 then - Hooks:Call("BlackMarketGUIOnPopulateMasksActionList", self, new_data) - end - - if i ~= 1 then - if 0 < managers.money:get_mask_sell_value(new_data.name, new_data.global_value) then - table.insert(new_data, "m_sell") - else - table.insert(new_data, "m_remove") - end - - end - - end - - if crafted.modded then - new_data.mini_icons = {} - local color_1 = tweak_data.blackmarket.colors[crafted.blueprint.color.id].colors[1] - local color_2 = tweak_data.blackmarket.colors[crafted.blueprint.color.id].colors[2] - table.insert(new_data.mini_icons, { - texture = false, - w = 16, - h = 16, - right = 0, - bottom = 0, - layer = 1, - color = color_2 - }) - table.insert(new_data.mini_icons, { - texture = false, - w = 16, - h = 16, - right = 18, - bottom = 0, - layer = 1, - color = color_1 - }) - if locked_parts.color then - local texture = self:get_lock_icon({ - global_value = locked_parts.color - }) - table.insert(new_data.mini_icons, { - texture = texture, - w = 32, - h = 32, - right = 2, - bottom = -5, - layer = 2, - color = tweak_data.screen_colors.important_1 - }) - end - - local pattern = crafted.blueprint.pattern.id - if pattern == "solidfirst" or pattern == "solidsecond" then - else - local material_id = crafted.blueprint.material.id - guis_catalog = "guis/" - local bundle_folder = tweak_data.blackmarket.materials[material_id] and tweak_data.blackmarket.materials[material_id].texture_bundle_folder - if bundle_folder then - guis_catalog = guis_catalog .. "dlcs/" .. tostring(bundle_folder) .. "/" - end - - local right = -3 - local bottom = 38 - (NOT_WIN_32 and 20 or 10) - local w = 42 - local h = 42 - table.insert(new_data.mini_icons, { - texture = guis_catalog .. "textures/pd2/blackmarket/icons/materials/" .. material_id, - right = right, - bottom = bottom, - w = w, - h = h, - layer = 1, - stream = true - }) - if locked_parts.material then - local texture = self:get_lock_icon({ - global_value = locked_parts.material - }) - table.insert(new_data.mini_icons, { - texture = texture, - w = 32, - h = 32, - right = right + (w - 32) / 2, - bottom = bottom + (h - 32) / 2, - layer = 2, - color = tweak_data.screen_colors.important_1 - }) - end - - end - - do - local right = -3 - local bottom = math.round(mini_icon_helper - 6 - 6 - 42) - local w = 42 - local h = 42 - table.insert(new_data.mini_icons, { - texture = tweak_data.blackmarket.textures[pattern].texture, - right = right, - bottom = bottom, - w = h, - h = w, - layer = 1, - stream = true, - render_template = Idstring("VertexColorTexturedPatterns") - }) - if locked_parts.pattern then - local texture = self:get_lock_icon({ - global_value = locked_parts.pattern - }) - table.insert(new_data.mini_icons, { - texture = texture, - w = 32, - h = 32, - right = right + (w - 32) / 2, - bottom = bottom + (h - 32) / 2, - layer = 2, - color = tweak_data.screen_colors.important_1 - }) - end - - end - - new_data.mini_icons.borders = true - elseif i ~= 1 and managers.blackmarket:can_modify_mask(i) and managers.blackmarket:got_new_drop("normal", "mask_mods", crafted.mask_id) then - new_data.mini_icons = new_data.mini_icons or {} - table.insert(new_data.mini_icons, { - name = "new_drop", - texture = "guis/textures/pd2/blackmarket/inv_newdrop", - right = 0, - top = 0, - layer = 1, - w = 16, - h = 16, - stream = false, - visible = true - }) - new_data.new_drop_data = {} - end - - data[index] = new_data - - end - - local can_buy_masks = true - for i = 1, max_items do - if not data[i] then - index = i + start_crafted_item - 1 - can_buy_masks = managers.blackmarket:is_mask_slot_unlocked(i) - new_data = {} - if can_buy_masks then - new_data.name = "bm_menu_btn_buy_new_mask" - new_data.name_localized = managers.localization:text("bm_menu_empty_mask_slot") - new_data.mid_text = {} - new_data.mid_text.noselected_text = new_data.name_localized - new_data.mid_text.noselected_color = tweak_data.screen_colors.button_stage_3 - if not currently_holding or not new_data.mid_text.noselected_text then - end - - new_data.mid_text.selected_text = managers.localization:text("bm_menu_btn_buy_new_mask") - new_data.mid_text.selected_color = currently_holding and new_data.mid_text.noselected_color or tweak_data.screen_colors.button_stage_2 - new_data.empty_slot = true - new_data.category = "masks" - new_data.slot = index - new_data.unlocked = true - new_data.equipped = false - new_data.num_backs = 0 - new_data.cannot_buy = not can_buy_masks - if currently_holding then - if i ~= 1 then - new_data.selected_text = managers.localization:to_upper_text("bm_menu_btn_place_mask") - end - - if i ~= 1 then - table.insert(new_data, "m_place") - end - - table.insert(new_data, "i_stop_move") - else - table.insert(new_data, "em_buy") - end - - if index ~= 1 and managers.blackmarket:got_new_drop(nil, "mask_buy", nil) then - new_data.mini_icons = new_data.mini_icons or {} - table.insert(new_data.mini_icons, { - name = "new_drop", - texture = "guis/textures/pd2/blackmarket/inv_newdrop", - right = 0, - top = 0, - layer = 1, - w = 16, - h = 16, - stream = false, - visible = false - }) - new_data.new_drop_data = {} - end - - else - new_data.name = "bm_menu_btn_buy_mask_slot" - new_data.name_localized = managers.localization:text("bm_menu_locked_mask_slot") - new_data.empty_slot = true - new_data.category = "masks" - new_data.slot = index - new_data.unlocked = true - new_data.equipped = false - new_data.num_backs = 0 - new_data.lock_texture = "guis/textures/pd2/blackmarket/money_lock" - new_data.lock_color = tweak_data.screen_colors.button_stage_3 - new_data.lock_shape = { - w = 32, - h = 32, - x = 0, - y = -32 - } - new_data.locked_slot = true - new_data.dlc_locked = managers.experience:cash_string(managers.money:get_buy_mask_slot_price()) - new_data.mid_text = {} - new_data.mid_text.noselected_text = new_data.name_localized - new_data.mid_text.noselected_color = tweak_data.screen_colors.button_stage_3 - new_data.mid_text.is_lock_same_color = true - if currently_holding then - new_data.mid_text.selected_text = new_data.mid_text.noselected_text - new_data.mid_text.selected_color = new_data.mid_text.noselected_color - table.insert(new_data, "i_stop_move") - elseif managers.money:can_afford_buy_mask_slot() then - new_data.mid_text.selected_text = managers.localization:text("bm_menu_btn_buy_mask_slot") - new_data.mid_text.selected_color = tweak_data.screen_colors.button_stage_2 - table.insert(new_data, "em_unlock") - else - new_data.mid_text.selected_text = managers.localization:text("bm_menu_cannot_buy_mask_slot") - new_data.mid_text.selected_color = tweak_data.screen_colors.important_1 - new_data.dlc_locked = new_data.dlc_locked .. " " .. managers.localization:to_upper_text("bm_menu_cannot_buy_mask_slot") - new_data.mid_text.lock_noselected_color = tweak_data.screen_colors.important_1 - new_data.cannot_buy = true - end - - end - - data[i] = new_data - end - - end - - end) - - if not success then Print(err) end - -end - -Hooks:RegisterHook("BlackMarketGUIOnPopulateMods") -Hooks:RegisterHook("BlackMarketGUIOnPopulateModsActionList") -function BlackMarketGui.populate_mods(self, data) - - local success, err = pcall(function() - - Hooks:Call("BlackMarketGUIOnPopulateMods", self, data) - - local new_data = {} - local default_mod = data.on_create_data.default_mod - local global_values = managers.blackmarket:get_crafted_category(data.prev_node_data.category)[data.prev_node_data.slot].global_values or {} - local gvs = {} - local mod_t = {} - local num_steps = #data.on_create_data - local achievement_tracker = tweak_data.achievement.weapon_part_tracker - local guis_catalog = "guis/" - - for index, mod_t in ipairs(data.on_create_data) do - - local mod_name = mod_t[1] - local mod_default = mod_t[2] - local mod_global_value = mod_t[3] or "normal" - guis_catalog = "guis/" - local bundle_folder = tweak_data.blackmarket.weapon_mods[mod_name] and tweak_data.blackmarket.weapon_mods[mod_name].texture_bundle_folder - if bundle_folder then - guis_catalog = guis_catalog .. "dlcs/" .. tostring(bundle_folder) .. "/" - end - - new_data = {} - new_data.name = mod_name or data.prev_node_data.name - if not mod_name or not managers.weapon_factory:get_part_name_by_part_id(mod_name) then - end - - new_data.name_localized = managers.weapon_factory:get_part_name_by_part_id(mod_name) - new_data.category = data.prev_node_data.category - new_data.bitmap_texture = guis_catalog .. "textures/pd2/blackmarket/icons/mods/" .. new_data.name - new_data.slot = not data.slot and data.prev_node_data and data.prev_node_data.slot - new_data.global_value = mod_global_value - new_data.unlocked = mod_default or managers.blackmarket:get_item_amount(new_data.global_value, "weapon_mods", new_data.name, true) - new_data.equipped = false - new_data.stream = true - new_data.default_mod = default_mod - new_data.is_internal = tweak_data.weapon.factory:is_part_internal(new_data.name) - new_data.free_of_charge = tweak_data.blackmarket.weapon_mods[mod_name] and tweak_data.blackmarket.weapon_mods[mod_name].is_a_unlockable - new_data.unlock_tracker = achievement_tracker[new_data.name] or false - if tweak_data.lootdrop.global_values[mod_global_value] and tweak_data.lootdrop.global_values[mod_global_value].dlc and not tweak_data.dlc[mod_global_value].free and not managers.dlc:has_dlc(mod_global_value) then - if type(new_data.unlocked) == "number" then - new_data.unlocked = -math.abs(new_data.unlocked) - end - new_data.unlocked = new_data.unlocked ~= 0 and new_data.unlocked or false - new_data.lock_texture = self:get_lock_icon(new_data) - new_data.dlc_locked = tweak_data.lootdrop.global_values[new_data.global_value].unlock_id or "bm_menu_dlc_locked" - end - - local weapon_id = managers.blackmarket:get_crafted_category(new_data.category)[new_data.slot].weapon_id - new_data.price = managers.money:get_weapon_modify_price(weapon_id, new_data.name, new_data.global_value) - new_data.can_afford = managers.money:can_afford_weapon_modification(weapon_id, new_data.name, new_data.global_value) - local font, font_size - local no_upper = false - if not new_data.lock_texture and (not new_data.unlocked or new_data.unlocked == 0) then - local selected_text, noselected_text - if not new_data.dlc_locked and new_data.unlock_tracker then - local text_id = "bm_menu_no_items" - local progress = "" - local stat = new_data.unlock_tracker.stat or false - local max_progress = new_data.unlock_tracker.max_progress or 0 - local award = new_data.unlock_tracker.award or false - if new_data.unlock_tracker.text_id then - if max_progress > 0 and stat then - local progress_left = max_progress - (managers.achievment:get_stat(stat) or 0) - if progress_left > 0 then - progress = tostring(progress_left) - text_id = new_data.unlock_tracker.text_id - font = small_font - font_size = small_font_size - no_upper = true - end - - elseif award then - local achievement = managers.achievment:get_info(award) - text_id = new_data.unlock_tracker.text_id - font = small_font - font_size = small_font_size - no_upper = true - end - - selected_text = managers.localization:text(text_id, {progress = progress}) - end - - end - - selected_text = selected_text or managers.localization:text("bm_menu_no_items") - noselected_text = selected_text - new_data.mid_text = {} - new_data.mid_text.selected_text = selected_text - new_data.mid_text.selected_color = tweak_data.screen_colors.text - new_data.mid_text.noselected_text = noselected_text - new_data.mid_text.noselected_color = tweak_data.screen_colors.text - new_data.mid_text.vertical = "center" - new_data.mid_text.font = font - new_data.mid_text.font_size = font_size - new_data.mid_text.no_upper = no_upper - new_data.lock_texture = true - end - - if mod_name then - local forbid = managers.blackmarket:can_modify_weapon(new_data.category, new_data.slot, new_data.name) - if forbid then - if type(new_data.unlocked) == "number" then - new_data.unlocked = -math.abs(new_data.unlocked) - else - new_data.unlocked = false - end - - new_data.lock_texture = self:get_lock_icon(new_data, "guis/textures/pd2/lock_incompatible") - new_data.mid_text = nil - new_data.conflict = managers.localization:text("bm_menu_" .. tostring(tweak_data.weapon.factory.parts[forbid].type)) - end - - local weapon = managers.blackmarket:get_crafted_category_slot(data.prev_node_data.category, data.prev_node_data.slot) or {} - local gadget - local mod_type = tweak_data.weapon.factory.parts[new_data.name].type - local sub_type = tweak_data.weapon.factory.parts[new_data.name].sub_type - local is_auto = weapon and tweak_data.weapon[weapon.weapon_id] and tweak_data.weapon[weapon.weapon_id].FIRE_MODE == "auto" - if mod_type == "gadget" then - gadget = sub_type - end - - local silencer = sub_type == "silencer" and true - local texture = managers.menu_component:get_texture_from_mod_type(mod_type, sub_type, gadget, silencer, is_auto) - new_data.desc_mini_icons = {} - if DB:has(Idstring("texture"), texture) then - table.insert(new_data.desc_mini_icons, { - texture = texture, - right = 0, - bottom = 0, - w = 16, - h = 16 - }) - end - - local is_gadget = false - if not new_data.conflict and new_data.unlocked and not is_gadget and not new_data.dlc_locked then - new_data.comparision_data = managers.blackmarket:get_weapon_stats_with_mod(new_data.category, new_data.slot, mod_name) - end - - if managers.blackmarket:got_new_drop(mod_global_value, "weapon_mods", mod_name) then - new_data.mini_icons = new_data.mini_icons or {} - table.insert(new_data.mini_icons, { - name = "new_drop", - texture = "guis/textures/pd2/blackmarket/inv_newdrop", - right = 0, - top = 0, - layer = 1, - w = 16, - h = 16, - stream = false - }) - new_data.new_drop_data = { - new_data.global_value or "normal", - "weapon_mods", - mod_name - } - end - - end - - if mod_name and new_data.unlocked then - if type(new_data.unlocked) ~= "number" or new_data.unlocked > 0 then - if new_data.can_afford then - table.insert(new_data, "wm_buy") - end - - table.insert(new_data, "wm_preview") - if not new_data.is_internal then - table.insert(new_data, "wm_preview_mod") - end - - else - table.insert(new_data, "wm_preview") - end - - end - - Hooks:Call("BlackMarketGUIOnPopulateModsActionList", self, new_data) - - data[index] = new_data - - end - - for i = 1, math.max(math.ceil(num_steps / 3), 3) * 3 do - if not data[i] then - new_data = {} - new_data.name = "empty" - new_data.name_localized = "" - new_data.category = data.category - new_data.slot = i - new_data.unlocked = true - new_data.equipped = false - data[i] = new_data - end - - end - - local weapon_blueprint = managers.blackmarket:get_weapon_blueprint(data.prev_node_data.category, data.prev_node_data.slot) or {} - local equipped - for i, mod in ipairs(data) do - for k, weapon_mod in ipairs(weapon_blueprint) do - - if mod.name == weapon_mod and (not global_values[weapon_mod] or global_values[weapon_mod] == data[i].global_value) then - equipped = i - end - - end - end - - if equipped then - - data[equipped].equipped = true - data[equipped].unlocked = data[equipped].unlocked or true - data[equipped].mid_text = nil - data[equipped].lock_texture = nil - - for i = 1, #data[equipped] do - table.remove(data[equipped], 1) - end - - data[equipped].price = 0 - data[equipped].can_afford = true - - table.insert(data[equipped], "wm_remove_buy") - - if not data[equipped].is_internal then - table.insert(data[equipped], "wm_remove_preview_mod") - table.insert(data[equipped], "wm_remove_preview") - else - table.insert(data[equipped], "wm_preview") - end - - Hooks:Call("BlackMarketGUIOnPopulateModsActionList", self, data[equipped]) - - local factory = tweak_data.weapon.factory.parts[data[equipped].name] - if data.name == "sight" and factory and factory.texture_switch then - - table.insert(data[equipped], "wm_reticle_switch_menu") - local reticle_texture = managers.blackmarket:get_part_texture_switch(data[equipped].category, data[equipped].slot, data[equipped].name) - if reticle_texture and reticle_texture ~= "" then - data[equipped].mini_icons = data[equipped].mini_icons or {} - table.insert(data[equipped].mini_icons, { - texture = reticle_texture, - right = 1, - bottom = 1, - layer = 2, - w = 30, - h = 30, - stream = true, - blend_mode = "add" - }) - table.insert(data[equipped].mini_icons, { - color = Color.black, - right = -5, - bottom = -5, - layer = 0, - alpha = 0.4, - w = 42, - h = 42, - borders = true - }) - end - - end - - if not data[equipped].conflict then - - if data[equipped].default_mod then - data[equipped].comparision_data = managers.blackmarket:get_weapon_stats_with_mod(data[equipped].category, data[equipped].slot, data[equipped].default_mod) - else - data[equipped].comparision_data = managers.blackmarket:get_weapon_stats_without_mod(data[equipped].category, data[equipped].slot, data[equipped].name) - end - - end - - end - - end) - if not success then Print("[Error] " .. err) end - -end - -Hooks:RegisterHook("BlackMarketGUIOnPopulateMaskMods") -Hooks:RegisterHook("BlackMarketGUIOnPopulateMaskModsActionList") -function BlackMarketGui.populate_choose_mask_mod(self, data) - - local success, err = pcall(function() - - Hooks:Call("BlackMarketGUIOnPopulateMaskMods", self, data) - - local new_data = {} - local index = 1 - local equipped_mod = managers.blackmarket:customize_mask_category_id(data.category) - local guis_catalog = "guis/" - - for k, mods in pairs(data.on_create_data) do - - guis_catalog = "guis/" - local bundle_folder = tweak_data.blackmarket[data.category][mods.id] and tweak_data.blackmarket[data.category][mods.id].texture_bundle_folder - if bundle_folder then - guis_catalog = guis_catalog .. "dlcs/" .. tostring(bundle_folder) .. "/" - end - - new_data = {} - new_data.name = mods.id - new_data.name_localized = managers.localization:text(tweak_data.blackmarket[data.category][new_data.name].name_id) - new_data.category = data.category - new_data.slot = index - new_data.prev_slot = data.prev_node_data and data.prev_node_data.slot - new_data.unlocked = mods.default or mods.amount - new_data.amount = mods.amount or 0 - new_data.equipped = equipped_mod == mods.id - new_data.equipped_text = managers.localization:text("bm_menu_chosen") - new_data.mods = mods - new_data.stream = data.category ~= "colors" - new_data.global_value = mods.global_value - local is_locked = false - if new_data.amount < 1 and mods.id ~= "plastic" and mods.id ~= "no_color_full_material" and not mods.free_of_charge then - if type(new_data.unlocked) == "number" then - new_data.unlocked = -math.abs(new_data.unlocked) - end - new_data.lock_texture = true - new_data.dlc_locked = "bm_menu_amount_locked" - is_locked = true - end - if tweak_data.lootdrop.global_values[new_data.global_value] and tweak_data.lootdrop.global_values[new_data.global_value].dlc and not tweak_data.dlc[new_data.global_value].free and not managers.dlc:has_dlc(new_data.global_value) then - if type(new_data.unlocked) == "number" then - new_data.unlocked = -math.abs(new_data.unlocked) - end - new_data.lock_texture = self:get_lock_icon(new_data) - new_data.dlc_locked = tweak_data.lootdrop.global_values[new_data.global_value].unlock_id or "bm_menu_dlc_locked" - is_locked = true - end - - if data.category == "colors" then - new_data.bitmap_texture = "guis/textures/pd2/blackmarket/icons/colors/color_bg" - new_data.extra_bitmaps = {} - table.insert(new_data.extra_bitmaps, "guis/textures/pd2/blackmarket/icons/colors/color_02") - table.insert(new_data.extra_bitmaps, "guis/textures/pd2/blackmarket/icons/colors/color_01") - new_data.extra_bitmaps_colors = {} - table.insert(new_data.extra_bitmaps_colors, tweak_data.blackmarket.colors[new_data.name].colors[2]) - table.insert(new_data.extra_bitmaps_colors, tweak_data.blackmarket.colors[new_data.name].colors[1]) - elseif data.category == "textures" then - new_data.bitmap_texture = tweak_data.blackmarket[data.category][mods.id].texture - new_data.render_template = Idstring("VertexColorTexturedPatterns") - else - new_data.bitmap_texture = guis_catalog .. "textures/pd2/blackmarket/icons/" .. tostring(data.category) .. "/" .. new_data.name - if mods.bitmap_texture_override then - new_data.bitmap_texture = guis_catalog .. "textures/pd2/blackmarket/icons/" .. tostring(data.category) .. "/" .. mods.bitmap_texture_override - end - end - - if managers.blackmarket:got_new_drop(new_data.global_value or "normal", new_data.category, new_data.name) then - new_data.mini_icons = new_data.mini_icons or {} - table.insert(new_data.mini_icons, { - name = "new_drop", - texture = "guis/textures/pd2/blackmarket/inv_newdrop", - right = 0, - top = 0, - layer = 1, - w = 16, - h = 16, - stream = false - }) - new_data.new_drop_data = { - new_data.global_value or "normal", - new_data.category, - new_data.name - } - end - - new_data.btn_text_params = { - type = managers.localization:text("bm_menu_" .. data.category) - } - if not is_locked then - - table.insert(new_data, "mp_choose") - table.insert(new_data, "mp_preview") - - end - - if managers.blackmarket:can_finish_customize_mask() and managers.blackmarket:can_afford_customize_mask() then - table.insert(new_data, "mm_buy") - end - - Hooks:Call("BlackMarketGUIOnPopulateMaskModsActionList", self, new_data) - - data[index] = new_data - index = index + 1 - - end - - if #data == 0 then - new_data = {} - new_data.name = "bm_menu_nothing" - new_data.empty_slot = true - new_data.category = data.category - new_data.slot = 1 - new_data.unlocked = true - new_data.can_afford = true - new_data.equipped = false - data[1] = new_data - end - - local max_mask_mods = #data.on_create_data - for i = 1, math.ceil(max_mask_mods / data.override_slots[1]) * data.override_slots[1] do - if not data[i] then - new_data = {} - new_data.name = "empty" - new_data.name_localized = "" - new_data.category = data.category - new_data.slot = i - new_data.unlocked = true - new_data.equipped = false - data[i] = new_data - end - - end - - end) - if not success then Print("[Error] " .. err) end - -end - - -Hooks:RegisterHook("BlackMarketGUIStartPageData") -function BlackMarketGui._start_page_data(self) - - local data = {} - table.insert(data, { - name = "bm_menu_primaries", - category = "primaries", - on_create_func_name = "populate_primaries", - identifier = self.identifiers.weapon, - override_slots = {3, 3} - }) - table.insert(data, { - name = "bm_menu_secondaries", - category = "secondaries", - on_create_func_name = "populate_secondaries", - identifier = self.identifiers.weapon, - override_slots = {3, 3} - }) - table.insert(data, { - name = "bm_menu_melee_weapons", - category = "melee_weapons", - on_create_func_name = "populate_melee_weapons", - allow_preview = true, - override_slots = {3, 3}, - identifier = Idstring("melee_weapon") - }) - table.insert(data, { - name = "bm_menu_armors", - category = "armors", - on_create_func_name = "populate_armors", - override_slots = {4, 2}, - identifier = self.identifiers.armor - }) - table.insert(data, { - name = "bm_menu_deployables", - category = "deployables", - on_create_func_name = "populate_deployables", - override_slots = {4, 2}, - identifier = Idstring("deployable") - }) - table.insert(data, { - name = "bm_menu_masks", - category = "masks", - on_create_func_name = "populate_masks", - identifier = self.identifiers.mask, - override_slots = {3, 3}, - start_crafted_item = 1 - }) - if not managers.network:session() then - table.insert(data, { - name = "bm_menu_characters", - category = "characters", - on_create_func_name = "populate_characters", - override_slots = {4, 2}, - identifier = self.identifiers.character - }) - end - - Hooks:Call("BlackMarketGUIStartPageData", self, data) - - data.topic_id = "menu_inventory" - self:_cleanup_blackmarket() - return data - -end - -Hooks:RegisterHook("BlackMarketGUIUpdateInfoText") -function BlackMarketGui.update_info_text(self) - self.orig.update_info_text(self) - Hooks:Call("BlackMarketGUIUpdateInfoText", self) -end - -function BlackMarketGui._update_info_text(self, slot_data, updated_texts, data, scale_override) - - local ignore_lock = false - local is_renaming_this = false - if data ~= nil then - ignore_lock = data.ignore_lock or ignore_lock - is_renaming_this = data.is_renaming_this or is_renaming_this - end - - if self._desc_mini_icons then - for _, gui_object in pairs(self._desc_mini_icons) do - self._panel:remove(gui_object[1]) - end - end - self._desc_mini_icons = {} - local desc_mini_icons = self._slot_data.desc_mini_icons - local info_box_panel = self._panel:child("info_box_panel") - if desc_mini_icons and 0 < table.size(desc_mini_icons) then - for _, mini_icon in pairs(desc_mini_icons) do - local new_icon = self._panel:bitmap({ - texture = mini_icon.texture, - x = info_box_panel:left() + 10 + mini_icon.right, - w = mini_icon.w or 32, - h = mini_icon.h or 32 - }) - table.insert(self._desc_mini_icons, {new_icon, 1}) - end - updated_texts[2].text = string.rep(" ", table.size(desc_mini_icons)) .. updated_texts[2].text - else - end - if not ignore_lock and slot_data.lock_texture and slot_data.lock_texture ~= true then - local new_icon = self._panel:bitmap({ - texture = slot_data.lock_texture, - x = info_box_panel:left() + 10, - w = 20, - h = 20, - color = self._info_texts[3]:color(), - blend_mode = "add" - }) - updated_texts[3].text = " " .. updated_texts[3].text - table.insert(self._desc_mini_icons, {new_icon, 2}) - else - end - if is_renaming_this and self._rename_info_text then - local text = self._renaming_item.custom_name ~= "" and self._renaming_item.custom_name or "##" .. tostring(slot_data.raw_name_localized) .. "##" - updated_texts[self._rename_info_text].text = text - updated_texts[self._rename_info_text].resource_color = tweak_data.screen_colors.text:with_alpha(0.35) - end - for id, _ in ipairs(self._info_texts) do - self:set_info_text(id, updated_texts[id].text, updated_texts[id].resource_color) - end - local _, _, _, th = self._info_texts[1]:text_rect() - self._info_texts[1]:set_h(th) - local y = self._info_texts[1]:bottom() - local title_offset = y - local bg = self._info_texts_bg[1] - if alive(bg) then - bg:set_shape(self._info_texts[1]:shape()) - end - local below_y - for i = 2, #self._info_texts do - local info_text = self._info_texts[i] - info_text:set_font_size(small_font_size) - _, _, _, th = info_text:text_rect() - info_text:set_y(y) - info_text:set_h(th) - if updated_texts[i].below_stats then - if slot_data.comparision_data and alive(self._stats_text_modslist) then - info_text:set_world_y(below_y or self._stats_text_modslist:world_top()) - below_y = (below_y or info_text:world_y()) + th - else - info_text:set_top((below_y or info_text:top()) + 20) - below_y = (below_y or info_text:top()) + th - end - end - local scale = 1 - if info_text:bottom() > self._info_texts_panel:h() then - scale = self._info_texts_panel:h() / info_text:bottom() - end - - if scale_override then - if type(scale_override) == "number" then - scale = scale_override - end - if type(scale_override) == "table" then - scale = scale_override[i] - end - end - - info_text:set_font_size(small_font_size * scale) - _, _, _, th = info_text:text_rect() - info_text:set_h(th) - local bg = self._info_texts_bg[i] - if alive(bg) then - bg:set_shape(info_text:shape()) - end - y = info_text:bottom() - end - for _, desc_mini_icon in ipairs(self._desc_mini_icons) do - desc_mini_icon[1]:set_y(title_offset) - desc_mini_icon[1]:set_world_top(self._info_texts[desc_mini_icon[2]]:world_bottom() + (2 - (desc_mini_icon[2] - 1) * 3)) - end - if is_renaming_this and self._rename_info_text and self._rename_caret then - local info_text = self._info_texts[self._rename_info_text] - local x, y, w, h = info_text:text_rect() - if self._renaming_item.custom_name == "" then - w = 0 - end - self._rename_caret:set_w(2) - self._rename_caret:set_h(h) - self._rename_caret:set_world_position(x + w, y) - end - -end - -Hooks:RegisterHook("BlackMarketGUIOnPopulateBuyMasks") -Hooks:RegisterHook("BlackMarketGUIOnPopulateBuyMasksActionList") -function BlackMarketGui.populate_buy_mask(self, data) - - Hooks:Call("BlackMarketGUIOnPopulateBuyMasks", self, data) - - local new_data = {} - local guis_catalog = "guis/" - local max_masks = #data.on_create_data - - for i = 1, max_masks do - data[i] = nil - end - - for i = 1, #data.on_create_data do - - guis_catalog = "guis/" - - local bundle_folder = tweak_data.blackmarket.masks[data.on_create_data[i].mask_id] and tweak_data.blackmarket.masks[data.on_create_data[i].mask_id].texture_bundle_folder - if bundle_folder then - guis_catalog = guis_catalog .. "dlcs/" .. tostring(bundle_folder) .. "/" - end - - new_data = {} - new_data.name = data.on_create_data[i].mask_id - new_data.name_localized = managers.localization:text(tweak_data.blackmarket.masks[new_data.name].name_id) - new_data.category = data.category - new_data.slot = data.prev_node_data and data.prev_node_data.slot - new_data.global_value = data.on_create_data[i].global_value - new_data.unlocked = managers.blackmarket:get_item_amount(new_data.global_value, "masks", new_data.name, true) or 0 - new_data.equipped = false - new_data.num_backs = data.prev_node_data.num_backs + 1 - new_data.bitmap_texture = guis_catalog .. "textures/pd2/blackmarket/icons/masks/" .. new_data.name - new_data.stream = false - - if not new_data.global_value then - Application:debug("BlackMarketGui:populate_buy_mask( data ) Missing global value on mask", new_data.name) - end - - if tweak_data.lootdrop.global_values[new_data.global_value] and tweak_data.lootdrop.global_values[new_data.global_value].dlc and not tweak_data.dlc[new_data.global_value].free and not managers.dlc:has_dlc(new_data.global_value) then - if type(new_data.unlocked) == "number" then - new_data.unlocked = -math.abs(new_data.unlocked) - end - new_data.lock_texture = self:get_lock_icon(new_data) - new_data.dlc_locked = tweak_data.lootdrop.global_values[new_data.global_value].unlock_id or "bm_menu_dlc_locked" - end - - if tweak_data.blackmarket.masks[new_data.name].infamy_lock then - - local infamy_lock = tweak_data.blackmarket.masks[new_data.name].infamy_lock - local is_unlocked = managers.infamy:owned(infamy_lock) - if not is_unlocked then - if type(new_data.unlocked) == "number" then - new_data.unlocked = -math.abs(new_data.unlocked) - end - new_data.lock_texture = "guis/textures/pd2/lock_infamy" - new_data.infamy_lock = infamy_lock - end - - end - - if new_data.unlocked and new_data.unlocked > 0 then - - table.insert(new_data, "bm_buy") - table.insert(new_data, "bm_preview") - if 0 < managers.money:get_mask_sell_value(new_data.name, new_data.global_value) then - table.insert(new_data, "bm_sell") - end - - else - - new_data.mid_text = "" - new_data.lock_texture = new_data.lock_texture or true - - end - - Hooks:Call("BlackMarketGUIOnPopulateBuyMasksActionList", self, new_data) - - if managers.blackmarket:got_new_drop(new_data.global_value or "normal", "masks", new_data.name) then - - new_data.mini_icons = new_data.mini_icons or {} - table.insert(new_data.mini_icons, { - name = "new_drop", - texture = "guis/textures/pd2/blackmarket/inv_newdrop", - right = 0, - top = 0, - layer = 1, - w = 16, - h = 16, - stream = false - }) - new_data.new_drop_data = { - new_data.global_value or "normal", - "masks", - new_data.name - } - - end - - data[i] = new_data - - end - - local max_page = data.override_slots[1] * data.override_slots[2] - for i = 1, math.max(math.ceil(max_masks / data.override_slots[1]) * data.override_slots[1], max_page) do - - if not data[i] then - new_data = {} - new_data.name = "empty" - new_data.name_localized = "" - new_data.category = data.category - new_data.slot = i - new_data.unlocked = true - new_data.equipped = false - data[i] = new_data - end - - end - -end - -Hooks:RegisterHook("BlackMarketGUIChooseMaskPartCallback") -function BlackMarketGui.choose_mask_part_callback(self, data) - local r = Hooks:ReturnCall("BlackMarketGUIChooseMaskPartCallback", self, data) - if r then - return - end - return self.orig.choose_mask_part_callback(self, data) -end - -Hooks:RegisterHook("BlackMarketGUIMouseReleased") -function BlackMarketGui.mouse_released(self, o, button, x, y) - if not self._enabled then - return - end - self.orig.mouse_released(self, o, button, x, y) - Hooks:Call("BlackMarketGUIMouseReleased", self, o, button, x, y) -end - -Hooks:RegisterHook("BlackMarketGUIOnPreviewWeapon") -function BlackMarketGui._preview_weapon(self, data) - self.orig._preview_weapon(self, data) - Hooks:Call("BlackMarketGUIOnPreviewWeapon", self, data) -end --- END OF FILE diff --git a/GoonBase/lua/BlackMarketManager.lua b/GoonBase/lua/BlackMarketManager.lua deleted file mode 100644 index c19442d..0000000 --- a/GoonBase/lua/BlackMarketManager.lua +++ /dev/null @@ -1,398 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( BlackMarketManager ) - -local INV_TO_CRAFT = Idstring("inventory_to_crafted") -local CRAFT_TO_INV = Idstring("crafted_to_inventroy") -local INV_ADD = Idstring("add_to_inventory") -local INV_REMOVE = Idstring("remove_from_inventory") -local CRAFT_ADD = Idstring("add_to_crafted") -local CRAFT_REMOVE = Idstring("remove_from_crafted") - -Hooks:RegisterHook("BlackMarketManagerPostSetup") -function BlackMarketManager._setup(self) - self.orig._setup(self) - Hooks:Call("BlackMarketManagerPostSetup", self) -end - -Hooks:RegisterHook("BlackMarketManagerModifyGetInventoryCategory") -function BlackMarketManager.get_inventory_category(self, category) - - local t = {} - - for global_value, categories in pairs(self._global.inventory) do - if categories[category] then - - for id, amount in pairs(categories[category]) do - table.insert(t, { - id = id, - global_value = global_value, - amount = amount - }) - end - - end - end - - Hooks:Call("BlackMarketManagerModifyGetInventoryCategory", self, category, t) - - return t - -end - -function BlackMarketManager:get_mods_on_weapon(category, slot) - local _global = Global.blackmarket_manager - if not _global.crafted_items[category] or not _global.crafted_items[category][slot] then - return - end - return _global.crafted_items[category][slot].blueprint -end - -function BlackMarketManager:on_traded_weapon(category, slot, remove_mods, skip_verification) - - local _global = Global.blackmarket_manager - if not _global.crafted_items[category] or not _global.crafted_items[category][slot] then - return - end - - local global_values = _global.crafted_items[category][slot].global_values or {} - local blueprint = _global.crafted_items[category][slot].blueprint - local default_blueprint = managers.weapon_factory:get_default_blueprint_by_factory_id( _global.crafted_items[category][slot].factory_id ) - - for i, default_part in ipairs(default_blueprint) do - table.delete(blueprint, default_part) - end - - -- Remove mods if we traded them - if remove_mods then - - for k, part_id in pairs(blueprint) do - local global_value = global_values[part_id] or "normal" - self:add_to_inventory(global_value, "weapon_mods", part_id, true) - self:alter_global_value_item(global_value, category, slot, part_id, CRAFT_REMOVE) - end - - end - - _global.crafted_items[category][slot] = nil - if not skip_verification then - self:_verfify_equipped_category(category) - if category == "primaries" then - self:_update_menu_scene_primary() - elseif category == "secondaries" then - self:_update_menu_scene_secondary() - end - - end - -end - -function BlackMarketManager:on_traded_mod(part_id) - - -- Find mod using part id - for k, v in pairs( tweak_data.blackmarket["weapon_mods"] ) do - if k == part_id then - - -- Get global value and amount of mod - local global_value = v.global_value or v.dlc or "normal" - local mod_amt = self:get_item_amount(global_value, "weapon_mods", part_id, true) - - -- Check if we have the mod before trying to remove it - if mod_amt > 0 then - self:remove_item(global_value, "weapon_mods", part_id) - end - - end - end - -end - -function BlackMarketManager:on_received_traded_mod(part_id) - - local success, err = pcall(function() - - -- Find mod using part id - for k, v in pairs( tweak_data.blackmarket["weapon_mods"] ) do - if k == part_id then - - -- Get global value and add mod - local global_value = v.global_value or v.dlc or "normal" - Print("global_value: " .. global_value) - managers.blackmarket:add_to_inventory(global_value, "weapon_mods", part_id, true) - - end - end - - end) - if not success then Print("[Error] " .. err) end - -end - -function BlackMarketManager:get_mask_slot_data(mask) - - local category = "masks" - if not Global.blackmarket_manager.crafted_items[category] then - return nil - end - - local slot - if type(mask) == "table" then - slot = mask.slot - end - if type(mask) == "number" then - slot = mask - end - - return Global.blackmarket_manager.crafted_items[category][slot] - -end - -function BlackMarketManager:get_mask_data(mask_id) - return tweak_data.blackmarket.masks[mask_id] -end - -function BlackMarketManager:get_mask_name(mask) - return managers.localization:text(tweak_data.blackmarket.masks[mask].name_id) -end - -function BlackMarketManager:get_mask_mod_data(mod_id, category) - return tweak_data.blackmarket[category][mod_id] -end - -function BlackMarketManager:get_mask_mod_name(mod, category) - return managers.localization:text(tweak_data.blackmarket[category][mod].name_id) -end - -function BlackMarketManager:get_free_mask_slot() - - local unlocked_mask_slots = Global.blackmarket_manager.unlocked_mask_slots - local mask_slots = Global.blackmarket_manager.crafted_items.masks - - for i = 1, #unlocked_mask_slots, 1 do - if mask_slots[i] == nil then - return i - end - end - - return nil - -end - -function BlackMarketManager:has_all_dlc_for_mask(mask_id) - - -- Get mask data - local mask_data = tweak_data.blackmarket.masks[mask_id] - if mask_data == nil then - return - end - - -- Check if user has DLC for mask - local dlc = mask_data.dlc - if dlc ~= nil then - if not managers.dlc:has_dlc(dlc) then - return dlc - end - end - - return true - -end - -function BlackMarketManager:has_all_dlc_for_mask_mod(mod_id, category) - - -- Get part data - local part_data = tweak_data.blackmarket[category][mod_id] - if part_data == nil then - return - end - local part_global_value = part_data.global_value or part_data.dlc or "normal" - - -- Check user has DLC - local dlc = part_data.dlc - if dlc ~= nil then - if not managers.dlc:has_dlc(dlc) then - return dlc - end - end - - return true - -end - -function BlackMarketManager:has_all_dlc_for_mask_and_parts(mask_id, material, pattern, color) - - -- Get mask part data - local mask_data = tweak_data.blackmarket.masks[mask_id] - local material_data = tweak_data.blackmarket.materials[material] - local pattern_data = tweak_data.blackmarket.textures[pattern] - local color_data = tweak_data.blackmarket.colors[color] - - if mask_data == nil then - return - end - if material_data == nil then - return - end - if pattern_data == nil then - return - end - if color_data == nil then - return - end - - -- Get DLCs - local dlcs_to_test = {} - if mask_data.dlc ~= nil then - table.insert( dlcs_to_test, mask_data.dlc ) - end - if material_data.dlc ~= nil then - table.insert( dlcs_to_test, material_data.dlc ) - end - if pattern_data.dlc ~= nil then - table.insert( dlcs_to_test, pattern_data.dlc ) - end - if color_data.dlc ~= nil then - table.insert( dlcs_to_test, color_data.dlc ) - end - - -- Test DLCs - local dlcs_failed = nil - for k, v in pairs(dlcs_to_test) do - if not managers.dlc:has_dlc(v) then - - if dlcs_failed == nil then - dlcs_failed = {} - end - - dlcs_failed[v] = true - - end - end - - -- Return result - if dlcs_failed ~= nil then - return dlcs_failed - end - return true - -end - -function BlackMarketManager:add_traded_mask_to_inventory(mask_id) - self:add_traded_mask_part_to_inventory(mask_id, "masks") -end - -function BlackMarketManager:add_traded_mask_part_to_inventory(part_id, category) - - -- Get part data - local part_data = tweak_data.blackmarket[category][part_id] - if part_data == nil then - return - end - local part_global_value = part_data.global_value or part_data.dlc or "normal" - if part_data.infamous then - part_global_value = "infamous" - end - - -- Add it to inventory - self:add_to_inventory(part_global_value, category, part_id, false) - -end - -function BlackMarketManager:add_traded_mask_to_free_slot(mask_id) - - -- Get free mask slot - local slot = self:get_free_mask_slot() - if slot == nil then - return - end - - -- Get mask data - local mask_data = tweak_data.blackmarket.masks[mask_id] - if mask_data == nil then - return - end - local mask_global_value = mask_data.global_value or mask_data.dlc or "normal" - - -- Add mask to inventory - self:on_buy_mask_to_inventory(mask_id, mask_global_value, slot) - -end - -function BlackMarketManager:add_traded_modded_mask_to_free_slot(mask_id, material, pattern, color) - - local success, err = pcall(function() - - -- Get free mask slot - local slot = self:get_free_mask_slot() - if slot == nil then - return - end - - -- Get mask part data - local mask_data = tweak_data.blackmarket.masks[mask_id] - local material_data = tweak_data.blackmarket.materials[material] - local pattern_data = tweak_data.blackmarket.textures[pattern] - local color_data = tweak_data.blackmarket.colors[color] - - if mask_data == nil then - return - end - if material_data == nil then - return - end - if pattern_data == nil then - return - end - if color_data == nil then - return - end - - -- Global values - local mask_global_value = mask_data.global_value or mask_data.dlc or "normal" - local material_global_value = material_data.global_value or material_data.dlc or "normal" - local pattern_global_value = pattern_data.global_value or pattern_data.dlc or "normal" - local color_global_value = color_data.global_value or color_data.dlc or "normal" - - -- Add mask to inventory - self:on_buy_mask_to_inventory(mask_id, mask_global_value, slot) - - -- Start customizing mask - self:start_customize_mask(slot) - - -- Setup customized mask data - self._customize_mask.slot = slot - self._customize_mask.materials = {id = material, global_value = material_global_value} - self._customize_mask.textures = {id = pattern, global_value = pattern_global_value} - self._customize_mask.colors = {id = color, global_value = color_global_value} - - -- Add cash to automatically craft mask - local amount = managers.money:get_mask_crafting_price_modified(self._customize_mask.mask_id, self._customize_mask.global_value, self:get_customized_mask_blueprint(), {}) - managers.money:_add_to_total(amount, {no_offshore = true}) - - -- Finish customizing mask and add to inventory - self:finish_customize_mask() - - end) - if not success then Print("[Error] " .. err) end - -end - -function BlackMarketManager:remove_mask_from_inventory(mask_slot) - - local category = mask_slot.category - local slot = mask_slot.slot - - managers.blackmarket:alter_global_value_item(mask_slot.global_value, category, slot, mask_slot.mask_id, INV_REMOVE) - Global.blackmarket_manager.crafted_items[category][slot] = nil - -end - -function BlackMarketManager:remove_mask_mod_from_inventory(mod_id, category) - local data = managers.blackmarket:get_mask_mod_data(mod_id, category) - local global_value = data.global_value or data.dlc or "normal" - managers.blackmarket:remove_item(global_value, category, mod_id) -end --- END OF FILE diff --git a/GoonBase/lua/BlackMarketTweakData.lua b/GoonBase/lua/BlackMarketTweakData.lua deleted file mode 100644 index 987a1bd..0000000 --- a/GoonBase/lua/BlackMarketTweakData.lua +++ /dev/null @@ -1,13 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( BlackMarketTweakData ) - -Hooks:RegisterHook("BlackMarketTweakDataPostInitGrenades") -function BlackMarketTweakData._init_grenades(self) - self.orig._init_grenades(self) - Hooks:Call("BlackMarketTweakDataPostInitGrenades", self) -end --- END OF FILE diff --git a/GoonBase/lua/CharacterTweakData.lua b/GoonBase/lua/CharacterTweakData.lua deleted file mode 100644 index 632b664..0000000 --- a/GoonBase/lua/CharacterTweakData.lua +++ /dev/null @@ -1,97 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( CharacterTweakData ) - -Hooks:RegisterHook("CharacterTweakDataPostMultiplyAllSpeeds") -function CharacterTweakData._multiply_all_speeds(this, walk_mul, run_mul) - this.orig._multiply_all_speeds(this, walk_mul, run_mul) - Hooks:Call("CharacterTweakDataPostMultiplyAllSpeeds", this, walk_mul, run_mul) -end - -Hooks:RegisterHook("CharacterTweakDataPostInitSecurity") -function CharacterTweakData._init_security(this, presets) - this.orig._init_security(this, presets) - Hooks:Call("CharacterTweakDataPostInitSecurity", this, presets) -end - -Hooks:RegisterHook("CharacterTweakDataPostInitGenSec") -function CharacterTweakData._init_gensec(this, presets) - this.orig._init_gensec(this, presets) - Hooks:Call("CharacterTweakDataPostInitGenSec", this, presets) -end - -Hooks:RegisterHook("CharacterTweakDataPostInitCop") -function CharacterTweakData._init_cop(this, presets) - this.orig._init_cop(this, presets) - Hooks:Call("CharacterTweakDataPostInitCop", this, presets) -end - -Hooks:RegisterHook("CharacterTweakDataPostInitFBI") -function CharacterTweakData._init_fbi(this, presets) - this.orig._init_fbi(this, presets) - Hooks:Call("CharacterTweakDataPostInitFBI", this, presets) -end - -Hooks:RegisterHook("CharacterTweakDataPostInitSWAT") -function CharacterTweakData._init_swat(this, presets) - this.orig._init_swat(this, presets) - Hooks:Call("CharacterTweakDataPostInitSWAT", this, presets) -end - -Hooks:RegisterHook("CharacterTweakDataPostInitHeavySWAT") -function CharacterTweakData._init_heavy_swat(this, presets) - this.orig._init_heavy_swat(this, presets) - Hooks:Call("CharacterTweakDataPostInitHeavySWAT", this, presets) -end - -Hooks:RegisterHook("CharacterTweakDataPostInitFBISWAT") -function CharacterTweakData._init_fbi_swat(this, presets) - this.orig._init_fbi_swat(this, presets) - Hooks:Call("CharacterTweakDataPostInitFBISWAT", this, presets) -end - -Hooks:RegisterHook("CharacterTweakDataPostInitFBIHeavySWAT") -function CharacterTweakData._init_fbi_heavy_swat(this, presets) - this.orig._init_fbi_heavy_swat(this, presets) - Hooks:Call("CharacterTweakDataPostInitFBIHeavySWAT", this, presets) -end - -Hooks:RegisterHook("CharacterTweakDataPostInitCitySWAT") -function CharacterTweakData._init_city_swat(this, presets) - this.orig._init_city_swat(this, presets) - Hooks:Call("CharacterTweakDataPostInitCitySWAT", this, presets) -end - -Hooks:RegisterHook("CharacterTweakDataPostInitSniper") -function CharacterTweakData._init_sniper(this, presets) - this.orig._init_sniper(this, presets) - Hooks:Call("CharacterTweakDataPostInitSniper", this, presets) -end - -Hooks:RegisterHook("CharacterTweakDataPostInitTank") -function CharacterTweakData._init_tank(this, presets) - this.orig._init_tank(this, presets) - Hooks:Call("CharacterTweakDataPostInitTank", this, presets) -end - -Hooks:RegisterHook("CharacterTweakDataPostInitCloaker") -function CharacterTweakData._init_spooc(this, presets) - this.orig._init_spooc(this, presets) - Hooks:Call("CharacterTweakDataPostInitCloaker", this, presets) -end - -Hooks:RegisterHook("CharacterTweakDataPostInitShield") -function CharacterTweakData._init_shield(this, presets) - this.orig._init_shield(this, presets) - Hooks:Call("CharacterTweakDataPostInitShield", this, presets) -end - -Hooks:RegisterHook("CharacterTweakDataPostInitTaser") -function CharacterTweakData._init_taser(this, presets) - this.orig._init_taser(this, presets) - Hooks:Call("CharacterTweakDataPostInitTaser", this, presets) -end --- END OF FILE diff --git a/GoonBase/lua/ChatManager.lua b/GoonBase/lua/ChatManager.lua deleted file mode 100644 index e0efd7a..0000000 --- a/GoonBase/lua/ChatManager.lua +++ /dev/null @@ -1,19 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( ChatManager ) - -Hooks:RegisterHook( "ChatManagerOnSendMessage" ) -function ChatManager.send_message(this, channel_id, sender, message) - Hooks:Call( "ChatManagerOnSendMessage", channel_id, sender, message ) - this.orig.send_message(this, channel_id, sender, message) -end - -Hooks:RegisterHook( "ChatManagerOnReceiveMessage" ) -function ChatManager._receive_message(this, channel_id, name, message, color, icon) - Hooks:Call( "ChatManagerOnReceiveMessage", channel_id, name, message, color, icon ) - this.orig._receive_message(this, channel_id, name, message, color, icon) -end --- END OF FILE diff --git a/GoonBase/lua/ContourExt.lua b/GoonBase/lua/ContourExt.lua deleted file mode 100644 index ad24618..0000000 --- a/GoonBase/lua/ContourExt.lua +++ /dev/null @@ -1,24 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( ContourExt ) - -Hooks:RegisterHook("ContourExtPreInitialize") -Hooks:RegisterHook("ContourExtPostInitialize") -function ContourExt.init(self, unit) - Hooks:Call("ContourExtPreInitialize", self, unit) - self.orig.init(self, unit) - Hooks:Call("ContourExtPostInitialize", self, unit) -end - -Hooks:RegisterHook("ContourExtPreAdd") -function ContourExt.add(self, type, sync, multiplier) - local r = Hooks:ReturnCall("ContourExtPreAdd", self, type, sync, multiplier) - if r ~= nil then - return - end - return self.orig.add(self, type, sync, multiplier) -end --- END OF FILE diff --git a/GoonBase/lua/CopActionHurt.lua b/GoonBase/lua/CopActionHurt.lua deleted file mode 100644 index ff85e16..0000000 --- a/GoonBase/lua/CopActionHurt.lua +++ /dev/null @@ -1,13 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( CopActionHurt ) - -Hooks:RegisterHook("CopActionHurtPostUpdateRagdolled") -function CopActionHurt._upd_ragdolled(self, t) - self.orig._upd_ragdolled(self, t) - Hooks:Call("CopActionHurtPostUpdateRagdolled", self, t) -end --- END OF FILE diff --git a/GoonBase/lua/CopDamage.lua b/GoonBase/lua/CopDamage.lua deleted file mode 100644 index eb16b69..0000000 --- a/GoonBase/lua/CopDamage.lua +++ /dev/null @@ -1,44 +0,0 @@ ----------- --- Payday 2 GoonMod, Public Release Beta 2, built on 1/4/2015 2:00:55 AM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( CopDamage ) - -Hooks:RegisterHook("CopDamagePostInitialize") -function CopDamage.init(self, unit) - self.orig.init(self, unit) - Hooks:Call("CopDamagePostInitialize", self, unit) -end - -Hooks:RegisterHook("CopDamageSetMoverCollisionState") -function CopDamage.set_mover_collision_state(self, state) - local r = Hooks:ReturnCall("CopDamageSetMoverCollisionState", self, state) - if r ~= nil then - state = r - end - self.orig.set_mover_collision_state(self, state) -end - -Hooks:RegisterHook("CopDamagePreDamageExplosion") -function CopDamage.damage_explosion(self, attack_data) - local r = Hooks:ReturnCall("CopDamagePreDamageExplosion", self, attack_data) - if r ~= nil then - return - end - self.orig.damage_explosion(self, attack_data) -end - -Hooks:RegisterHook("CopDamagePostDeath") -function CopDamage.die(self, variant) - self.orig.die(self, variant) - Hooks:Call("CopDamagePostDeath", self, variant) -end - -Hooks:RegisterHook("CopDamagePostDamageBullet") -function CopDamage.damage_bullet(self, attack_data) - local res = self.orig.damage_bullet(self, attack_data) - Hooks:Call("CopDamagePostDamageBullet", self, attack_data, res) - return res -end --- END OF FILE diff --git a/GoonBase/lua/CopInventory.lua b/GoonBase/lua/CopInventory.lua deleted file mode 100644 index 7cea995..0000000 --- a/GoonBase/lua/CopInventory.lua +++ /dev/null @@ -1,55 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( CopInventory ) - -Hooks:RegisterHook("CopInventoryDropShield") -function CopInventory.drop_shield(self) - - if alive(self._shield_unit) then - self._shield_unit:unlink() - if self._shield_unit:damage() then - self._shield_unit:damage():run_sequence_simple("enable_body") - end - end - - Hooks:Call("CopInventoryDropShield", self) - -end - -Hooks:RegisterHook("CopInventoryDestroyAllItems") -function CopInventory.destroy_all_items(self) - - CopInventory.super.destroy_all_items(self) - if alive(self._shield_unit) then - self._shield_unit:set_slot(0) - self._shield_unit = nil - end - - Hooks:Call("CopInventoryDestroyAllItems", self) - -end - -Hooks:RegisterHook("CopInventoryCheckSpawnShield") -function CopInventory._chk_spawn_shield(self, weapon_unit) - - -- self.orig._chk_spawn_shield(self, weapon_unit) - Hooks:Call("CopInventoryCheckSpawnShield", self, weapon_unit) - - if self._shield_unit_name and not alive(self._shield_unit) then - local align_name = self._shield_attach_point or Idstring("a_weapon_left_front") - local align_obj = self._unit:get_object(align_name) - self._shield_unit = World:spawn_unit(Idstring(self._shield_unit_name), align_obj:position(), align_obj:rotation()) - self._unit:link(align_name, self._shield_unit, self._shield_unit:orientation_object():name()) - self._shield_unit:set_enabled(false) - end - -end - -Hooks:RegisterHook("CopInventoryUpdate") -function CopInventory.update(self, unit, t, dt) - Hooks:Call("CopInventoryUpdate", self, unit, t, dt) -end --- END OF FILE diff --git a/GoonBase/lua/CoreMenuInput.lua b/GoonBase/lua/CoreMenuInput.lua deleted file mode 100644 index d741206..0000000 --- a/GoonBase/lua/CoreMenuInput.lua +++ /dev/null @@ -1,45 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -core:import("CoreMenuInput") - -CloneClass( CoreMenuInput.MenuInput ) -local MenuInput = CoreMenuInput.MenuInput - -function MenuInput.input_slider(self, item, controller) - - local slider_delay_down = 0.1 - local slider_delay_pressed = 0.2 - - if self:menu_right_input_bool() then - - item:increase() - self._logic:trigger_item(true, item) - self:set_axis_x_timer(slider_delay_down) - if self:menu_right_pressed() then - local percentage = item:percentage() - if percentage > 0 and percentage < 100 then - self:post_event("slider_increase") - end - self:set_axis_x_timer(slider_delay_pressed) - end - - elseif self:menu_left_input_bool() then - - item:decrease() - self._logic:trigger_item(true, item) - self:set_axis_x_timer(slider_delay_down) - if self:menu_left_pressed() then - self:set_axis_x_timer(slider_delay_pressed) - local percentage = item:percentage() - if percentage > 0 and percentage < 100 then - self:post_event("slider_decrease") - end - end - - end - -end --- END OF FILE diff --git a/GoonBase/lua/CoreMenuItem.lua b/GoonBase/lua/CoreMenuItem.lua deleted file mode 100644 index 7b87373..0000000 --- a/GoonBase/lua/CoreMenuItem.lua +++ /dev/null @@ -1,24 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -core:import("CoreMenuItem") - -CloneClass( CoreMenuItem.Item ) -local Item = CoreMenuItem.Item - -function Item.trigger(self) - self.orig.trigger(self) -end - -function Item.dirty(self) - - if self._parameters.type ~= "CoreMenuItemSlider.ItemSlider" or self._type ~= "slider" then - if self.dirty_callback then - self.dirty_callback(self) - end - end - -end --- END OF FILE diff --git a/GoonBase/lua/CoreMenuItemSlider.lua b/GoonBase/lua/CoreMenuItemSlider.lua deleted file mode 100644 index 722b89f..0000000 --- a/GoonBase/lua/CoreMenuItemSlider.lua +++ /dev/null @@ -1,28 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -core:import("CoreMenuItemSlider") - -CloneClass( CoreMenuItemSlider.ItemSlider ) -local ItemSlider = CoreMenuItemSlider.ItemSlider - -function ItemSlider.setup_gui(self, node, row_item) - local r = self.orig.setup_gui(self, node, row_item) - row_item.gui_slider_text:set_font_size( tweak_data.menu.stats_font_size ) - return r -end - -function ItemSlider.set_value(self, value) - self._value = math.min(math.max(self._min, value), self._max) - self:dirty() -end - -function ItemSlider.reload(self, row_item, node) - local r = self.orig.reload(self, row_item, node) - local value = self:show_value() and string.format("%.2f", math.round_with_precision(self:value(), 2)) or string.format("%.0f", self:percentage()) .. "%" - row_item.gui_slider_text:set_text(value) - return r -end --- END OF FILE diff --git a/GoonBase/lua/CoreMenuLogic.lua b/GoonBase/lua/CoreMenuLogic.lua deleted file mode 100644 index eb6073b..0000000 --- a/GoonBase/lua/CoreMenuLogic.lua +++ /dev/null @@ -1,30 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -core:import("CoreMenuLogic") - -CloneClass( CoreMenuLogic.Logic ) -local Logic = CoreMenuLogic.Logic - -function Logic.select_node(self, node_name, queue, ...) - -- Print("Logic.select_node") - self.orig.select_node(self, node_name, queue, ...) -end - -function Logic.select_item(self, item_name, queue) - -- Print("Logic.select_item") - self.orig.select_item(self, item_name, queue) -end - -function Logic.trigger_item(self, queue, item) - -- Print("Logic.trigger_item") - self.orig.trigger_item(self, queue, item) -end - -function Logic._trigger_item(self, item) - -- Print("Logic._trigger_item") - self.orig._trigger_item(self, item) -end --- END OF FILE diff --git a/GoonBase/lua/CriminalsManager.lua b/GoonBase/lua/CriminalsManager.lua deleted file mode 100644 index 7a493ef..0000000 --- a/GoonBase/lua/CriminalsManager.lua +++ /dev/null @@ -1,17 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( CriminalsManager ) - -Hooks:Call("CriminalsManagerNumberOfTakenCriminals") -function CriminalsManager.nr_taken_criminals(self) - local orig = self.orig.nr_taken_criminals(self) - local r = Hooks:ReturnCall("CriminalsManagerNumberOfTakenCriminals", self) - if r ~= nil then - return r - end - return orig -end --- END OF FILE diff --git a/GoonBase/lua/ElementLaserTrigger.lua b/GoonBase/lua/ElementLaserTrigger.lua deleted file mode 100644 index 2552e7e..0000000 --- a/GoonBase/lua/ElementLaserTrigger.lua +++ /dev/null @@ -1,19 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( ElementLaserTrigger ) - -Hooks:RegisterHook("ElementLaserTriggerPostInit") -function ElementLaserTrigger.init(self, ...) - self.orig.init(self, ...) - Hooks:Call("ElementLaserTriggerPostInit", self) -end - -Hooks:RegisterHook("ElementLaserTriggerUpdateDraw") -function ElementLaserTrigger.update_laser_draw(self, t, dt) - self.orig.update_laser_draw(self, t, dt) - Hooks:Call("ElementLaserTriggerUpdateDraw", self, t, dt) -end --- END OF FILE diff --git a/GoonBase/lua/ElementSpawnGrenade.lua b/GoonBase/lua/ElementSpawnGrenade.lua deleted file mode 100644 index ad81cf7..0000000 --- a/GoonBase/lua/ElementSpawnGrenade.lua +++ /dev/null @@ -1,11 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( ElementSpawnGrenade ) - -function ElementSpawnGrenade.on_executed(self, instigator) - self.orig.on_executed(self, instigator) -end --- END OF FILE diff --git a/GoonBase/lua/EnemyManager.lua b/GoonBase/lua/EnemyManager.lua deleted file mode 100644 index d49d16a..0000000 --- a/GoonBase/lua/EnemyManager.lua +++ /dev/null @@ -1,19 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( EnemyManager ) - -Hooks:RegisterHook( "EnemyManagerPreUpdateCorpseDisposal" ) -function EnemyManager._upd_corpse_disposal(this) - Hooks:Call("EnemyManagerPreUpdateCorpseDisposal", this) - this.orig._upd_corpse_disposal(this) -end - -Hooks:RegisterHook("EnemyManagerInitEnemyData") -function EnemyManager._init_enemy_data(self) - self.orig._init_enemy_data(self) - Hooks:Call("EnemyManagerInitEnemyData", self) -end --- END OF FILE diff --git a/GoonBase/lua/FPCameraPlayerBase.lua b/GoonBase/lua/FPCameraPlayerBase.lua deleted file mode 100644 index 00e21ac..0000000 --- a/GoonBase/lua/FPCameraPlayerBase.lua +++ /dev/null @@ -1,13 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( FPCameraPlayerBase ) - -Hooks:RegisterHook("FPCameraBaseStanceEnteredCallback") -function FPCameraPlayerBase.clbk_stance_entered(self, new_shoulder_stance, new_head_stance, new_vel_overshot, new_fov, new_shakers, stance_mod, duration_multiplier, duration) - Hooks:Call( "FPCameraBaseStanceEnteredCallback", self, new_shoulder_stance, new_head_stance, new_vel_overshot, new_fov, new_shakers, stance_mod, duration_multiplier, duration ) - self.orig.clbk_stance_entered(self, new_shoulder_stance, new_head_stance, new_vel_overshot, new_fov, new_shakers, stance_mod, duration_multiplier, duration) -end --- END OF FILE diff --git a/GoonBase/lua/FragGrenade.lua b/GoonBase/lua/FragGrenade.lua deleted file mode 100644 index 9ce229e..0000000 --- a/GoonBase/lua/FragGrenade.lua +++ /dev/null @@ -1,19 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( FragGrenade ) - -Hooks:RegisterHook("FragGrenadePostInit") -function FragGrenade.init(self, unit) - self.orig.init(self, unit) - Hooks:Call("FragGrenadePostInit", self, unit) -end - -Hooks:RegisterHook("FragGrenadeDetonate") -function FragGrenade._detonate(self) - self._detonate(self) - Hooks:Call("FragGrenadeDetonate") -end --- END OF FILE diff --git a/GoonBase/lua/GageAssignmentManager.lua b/GoonBase/lua/GageAssignmentManager.lua deleted file mode 100644 index db5c8bd..0000000 --- a/GoonBase/lua/GageAssignmentManager.lua +++ /dev/null @@ -1,13 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( GageAssignmentManager ) - -Hooks:RegisterHook("GageAssignmentManagerOnMissionCompleted") -function GageAssignmentManager.on_mission_completed(self) - Hooks:Call("GageAssignmentManagerOnMissionCompleted", self) - return self.orig.on_mission_completed(self) -end --- END OF FILE diff --git a/GoonBase/lua/GameSetup.lua b/GoonBase/lua/GameSetup.lua deleted file mode 100644 index fa91ab6..0000000 --- a/GoonBase/lua/GameSetup.lua +++ /dev/null @@ -1,13 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( GameSetup ) - -Hooks:RegisterHook("GameSetupUpdate") -function GameSetup.update(this, t, dt) - Hooks:Call("GameSetupUpdate", t, dt) - return this.orig.update(this, t, dt) -end --- END OF FILE diff --git a/GoonBase/lua/GameStateMachine.lua b/GoonBase/lua/GameStateMachine.lua deleted file mode 100644 index 6131a3a..0000000 --- a/GoonBase/lua/GameStateMachine.lua +++ /dev/null @@ -1,13 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( GameStateMachine ) - -Hooks:RegisterHook("GameStateMachineChangeStateByName") -function GameStateMachine.change_state_by_name(self, state_name, params) - Hooks:Call("GameStateMachineChangeStateByName", self, state_name, params) - self.orig.change_state_by_name(self, state_name, params) -end --- END OF FILE diff --git a/GoonBase/lua/GroupAIManager.lua b/GoonBase/lua/GroupAIManager.lua deleted file mode 100644 index 7ea9474..0000000 --- a/GoonBase/lua/GroupAIManager.lua +++ /dev/null @@ -1,12 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( GroupAIManager ) - -function GroupAIManager.set_state(self, name) - -- Print("Setting Group AI State to: " .. name) - self.orig.set_state(self, name) -end --- END OF FILE diff --git a/GoonBase/lua/GroupAIStateBase.lua b/GoonBase/lua/GroupAIStateBase.lua deleted file mode 100644 index 2c5fc43..0000000 --- a/GoonBase/lua/GroupAIStateBase.lua +++ /dev/null @@ -1,20 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( GroupAIStateBase ) - -function GroupAIStateBase.convert_hostage_to_criminal(self, unit, peer_unit) - unit:movement()._preconvert_team = unit:movement():team() - self.orig.convert_hostage_to_criminal(self, unit, peer_unit) -end - -function GroupAIStateBase.clbk_minion_dies(self, player_key, minion_unit, damage_info) - local _preconvert_team = minion_unit:movement()._preconvert_team - if _preconvert_team ~= nil then - minion_unit:movement():set_team( _preconvert_team ) - end - self.orig.clbk_minion_dies(self, player_key, minion_unit, damage_info) -end --- END OF FILE diff --git a/GoonBase/lua/GroupAIStateBesiege.lua b/GoonBase/lua/GroupAIStateBesiege.lua deleted file mode 100644 index 76f6279..0000000 --- a/GoonBase/lua/GroupAIStateBesiege.lua +++ /dev/null @@ -1,13 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( GroupAIStateBesiege ) - -Hooks:RegisterHook("GroupAIStateBesiegeInit") -function GroupAIStateBesiege.init(self) - self.orig.init(self) - Hooks:Call("GroupAIStateBesiegeInit", self) -end --- END OF FILE diff --git a/GoonBase/lua/GroupAITweakData.lua b/GoonBase/lua/GroupAITweakData.lua deleted file mode 100644 index f9c29c7..0000000 --- a/GoonBase/lua/GroupAITweakData.lua +++ /dev/null @@ -1,25 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( GroupAITweakData ) - -Hooks:RegisterHook( "GroupAITweakDataPostInitTaskData" ) -function GroupAITweakData._init_task_data(self, difficulty_index, difficulty) - self.orig._init_task_data(self, difficulty_index, difficulty) - Hooks:Call( "GroupAITweakDataPostInitTaskData", self, difficulty_index, difficulty ) -end - -Hooks:RegisterHook( "GroupAITweakDataPostInitUnitCategories" ) -function GroupAITweakData._init_unit_categories(self, difficulty_index) - self.orig._init_unit_categories(self, difficulty_index) - Hooks:Call( "GroupAITweakDataPostInitUnitCategories", self, difficulty_index ) -end - -Hooks:RegisterHook( "GroupAITweakDataPostInitEnemySpawnGroups" ) -function GroupAITweakData._init_enemy_spawn_groups(self, difficulty_index) - self.orig._init_enemy_spawn_groups(self, difficulty_index) - Hooks:Call( "GroupAITweakDataPostInitEnemySpawnGroups", self, difficulty_index ) -end --- END OF FILE diff --git a/GoonBase/lua/HUDManager.lua b/GoonBase/lua/HUDManager.lua deleted file mode 100644 index cf76e32..0000000 --- a/GoonBase/lua/HUDManager.lua +++ /dev/null @@ -1,40 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( HUDManager ) - -Hooks:RegisterHook("HUDManagerSetStaminaValue") -function HUDManager.set_stamina_value(this, value) - this.orig.set_stamina_value(this, value) - Hooks:PCall("HUDManagerSetStaminaValue", this, value) -end - -Hooks:RegisterHook("HUDManagerSetMaxStamina") -function HUDManager.set_max_stamina(this, value) - this.orig.set_max_stamina(this, value) - Hooks:PCall("HUDManagerSetMaxStamina", this, value) -end - -Hooks:RegisterHook("HUDManagerSetMugshotDowned") -function HUDManager.set_mugshot_downed(this, id) - this.orig.set_mugshot_downed(this, id) - Hooks:PCall("HUDManagerSetMugshotDowned", this, id) -end - -Hooks:RegisterHook("HUDManagerPreAddWaypoint") -function HUDManager.add_waypoint(self, id, data) - local r = Hooks:ReturnCall("HUDManagerPreAddWaypoint", self, id, data) - if r then - return - end - return self.orig.add_waypoint(self, id, data) -end - -Hooks:RegisterHook("HUDManagerPreAddNameLabel") -function HUDManager._add_name_label(self, data) - Hooks:Call("HUDManagerPreAddNameLabel", self, data) - return self.orig._add_name_label(self, data) -end --- END OF FILE diff --git a/GoonBase/lua/HUDManagerPD2.lua b/GoonBase/lua/HUDManagerPD2.lua deleted file mode 100644 index c031696..0000000 --- a/GoonBase/lua/HUDManagerPD2.lua +++ /dev/null @@ -1,156 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( HUDManager ) - -function HUDManager._create_teammates_panel(self, hud) - Print("HUDManager._create_teammates_panel(self, hud)") - self.orig._create_teammates_panel(self, hud) -end - -function HUDManager.remove_teammate_panel_by_name_id(self, name_id) - Print("HUDManager:remove_teammate_panel_by_name_id(" .. name_id .. ")") - self.orig.remove_teammate_panel_by_name_id(self, name_id) -end - -function HUDManager.remove_teammate_panel(self, id) - Print("HUDManager:remove_teammate_panel(" .. tostring(id) .. ")") - self.orig.remove_teammate_panel(self, id) -end - -function HUDManager:add_mugshot_by_unit(unit) - if unit:base().is_local_player then - return - end - local character_name = unit:base():nick_name() - local name_label_id = managers.hud:_add_name_label({name = character_name, unit = unit}) - unit:unit_data().name_label_id = name_label_id - local is_husk_player = unit:base().is_husk_player - local character_name_id = managers.criminals:character_name_by_unit(unit) - for i, data in ipairs(self._hud.mugshots) do - if data.character_name_id == character_name_id then - if is_husk_player and not data.peer_id then - -- self:_remove_mugshot(data.id) - break - else - unit:unit_data().mugshot_id = data.id - managers.hud:set_mugshot_normal(unit:unit_data().mugshot_id) - managers.hud:set_mugshot_armor(unit:unit_data().mugshot_id, 1) - managers.hud:set_mugshot_health(unit:unit_data().mugshot_id, 1) - return - end - end - end - local peer, peer_id - if is_husk_player then - peer = unit:network():peer() - peer_id = peer:id() - end - local use_lifebar = is_husk_player and true or false - local mugshot_id = managers.hud:add_mugshot({ - name = utf8.to_upper(character_name), - use_lifebar = use_lifebar, - peer_id = peer_id, - character_name_id = character_name_id - }) - unit:unit_data().mugshot_id = mugshot_id - if peer and peer:is_cheater() then - self:mark_cheater(peer_id) - end - return mugshot_id -end - -function HUDManager.add_teammate_panel(self, character_name, player_name, ai, peer_id) - - Print("HUDManager.add_teammate_panel (" .. tostring(character_name) .. " / " .. tostring(player_name) .. ")") - - for i, data in ipairs(self._hud.teammate_panels_data) do - Print(tostring(i) .. " / taken: " .. tostring(data.taken)) - if not data.taken then - - Print(tostring(i) .. " is not taken yet") - - self._teammate_panels[i]:add_panel() - self._teammate_panels[i]:set_peer_id(peer_id) - self._teammate_panels[i]:set_ai(ai) - self:set_teammate_callsign(i, ai and 5 or peer_id) - self:set_teammate_name(i, player_name) - self:set_teammate_state(i, ai and "ai" or "player") - - if peer_id then - - local peer_equipment = managers.player:get_synced_equipment_possession(peer_id) or {} - for equipment, amount in pairs(peer_equipment) do - self:add_teammate_special_equipment(i, { - id = equipment, - icon = tweak_data.equipments.specials[equipment].icon, - amount = amount - }) - end - - local peer_deployable_equipment = managers.player:get_synced_deployable_equipment(peer_id) - - if peer_deployable_equipment then - local icon = tweak_data.equipments[peer_deployable_equipment.deployable].icon - self:set_deployable_equipment(i, { - icon = icon, - amount = peer_deployable_equipment.amount - }) - end - - local peer_cable_ties = managers.player:get_synced_cable_ties(peer_id) - if peer_cable_ties then - local icon = tweak_data.equipments.specials.cable_tie.icon - self:set_cable_tie(i, { - icon = icon, - amount = peer_cable_ties.amount - }) - end - - local peer_grenades = managers.player:get_synced_grenades(peer_id) - if peer_grenades then - local icon = tweak_data.blackmarket.grenades[peer_grenades.grenade].icon - self:set_teammate_grenades(i, { - icon = icon, - amount = Application:digest_value(peer_grenades.amount, false) - }) - end - - end - - local unit = managers.player:player_unit(peer_id) - --local unit = managers.criminals:character_unit_by_name(character_name) - if alive(unit) then - local weapon = unit:inventory():equipped_unit() - if alive(weapon) then - local icon = weapon:base():weapon_tweak_data().hud_icon - local equipped_selection = unit:inventory():equipped_selection() - self:_set_teammate_weapon_selected(i, equipped_selection, icon) - end - end - - local peer_ammo_info = managers.player:get_synced_ammo_info(peer_id) - if peer_ammo_info then - for selection_index, ammo_info in pairs(peer_ammo_info) do - self:set_teammate_ammo_amount(i, selection_index, unpack(ammo_info)) - end - end - - local peer_carry_data = managers.player:get_synced_carry(peer_id) - - if peer_carry_data then - self:set_teammate_carry_info(i, peer_carry_data.carry_id, managers.loot:get_real_value(peer_carry_data.carry_id, peer_carry_data.multiplier)) - end - - data.taken = true - - Print("id: " .. tostring(i)) - return i - - end - end - -end --- END OF FILE diff --git a/GoonBase/lua/InfamyTweakData.lua b/GoonBase/lua/InfamyTweakData.lua deleted file mode 100644 index b75dcc6..0000000 --- a/GoonBase/lua/InfamyTweakData.lua +++ /dev/null @@ -1,11 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( InfamyTweakData ) - -function InfamyTweakData.init(self) - self.orig.init(self) -end --- END OF FILE diff --git a/GoonBase/lua/InteractionExt.lua b/GoonBase/lua/InteractionExt.lua deleted file mode 100644 index 2626c05..0000000 --- a/GoonBase/lua/InteractionExt.lua +++ /dev/null @@ -1,19 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( BaseInteractionExt ) - -local ids_contour_color = Idstring("contour_color") -local ids_contour_opacity = Idstring("contour_opacity") -Hooks:RegisterHook("BaseInteractionExtPreSetContour") -function BaseInteractionExt.set_contour(self, color, opacity) - local r = Hooks:ReturnCall("BaseInteractionExtPreSetContour", self, color, opacity) - if r ~= nil then - color = r.color - opacity = r.opacity - end - self.orig.set_contour(self, color, opacity) -end --- END OF FILE diff --git a/GoonBase/lua/JobManager.lua b/GoonBase/lua/JobManager.lua deleted file mode 100644 index 5ac8f2a..0000000 --- a/GoonBase/lua/JobManager.lua +++ /dev/null @@ -1,13 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( JobManager ) - -Hooks:RegisterHook("JobManagerOnSetNextInteruptStage") -function JobManager.set_next_interupt_stage(self, interupt) - self.orig.set_next_interupt_stage(self, interupt) - Hooks:Call("JobManagerOnSetNextInteruptStage", self, interupt) -end --- END OF FILE diff --git a/GoonBase/lua/LevelsTweakData.lua b/GoonBase/lua/LevelsTweakData.lua deleted file mode 100644 index b972e87..0000000 --- a/GoonBase/lua/LevelsTweakData.lua +++ /dev/null @@ -1,13 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( LevelsTweakData ) - -Hooks:RegisterHook("LevelsTweakDataInit") -function LevelsTweakData.init(self) - self.orig.init(self) - Hooks:Call("LevelsTweakDataInit", self) -end --- END OF FILE diff --git a/GoonBase/lua/LocalizationManager.lua b/GoonBase/lua/LocalizationManager.lua deleted file mode 100644 index 568610d..0000000 --- a/GoonBase/lua/LocalizationManager.lua +++ /dev/null @@ -1,24 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( LocalizationManager ) - -function LocalizationManager.text(this, str, macros) - - if _G.GoonBase.Localization[str] then - - local return_str =_G.GoonBase.Localization[str] - if macros and type(macros) == "table" then - for k, v in pairs( macros ) do - return_str = return_str:gsub( "$" .. k, v ) - end - end - return return_str - - end - return this.orig.text(this, str, macros) - -end --- END OF FILE diff --git a/GoonBase/lua/MaskExt.lua b/GoonBase/lua/MaskExt.lua deleted file mode 100644 index a60919b..0000000 --- a/GoonBase/lua/MaskExt.lua +++ /dev/null @@ -1,25 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( MaskExt ) - -function MaskExt.clbk_texture_loaded(self, async_clbk, tex_name) - if not alive(self._unit) then - return - end - for tex_id, texture_data in pairs(self._textures) do - if not texture_data.ready and tex_name == texture_data.name then - texture_data.ready = true - local new_texture = TextureCache:retrieve(tex_name, "normal") - for _, material in ipairs(self._materials) do - material:set_texture(tex_id == "pattern" and "material_texture" or "reflection_texture", new_texture) - end - TextureCache:unretrieve(tex_name) - TextureCache:unretrieve(tex_name) - end - end - self:_chk_load_complete(async_clbk) -end --- END OF FILE diff --git a/GoonBase/lua/MenuComponentManager.lua b/GoonBase/lua/MenuComponentManager.lua deleted file mode 100644 index e1eea60..0000000 --- a/GoonBase/lua/MenuComponentManager.lua +++ /dev/null @@ -1,13 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( MenuComponentManager ) - -Hooks:RegisterHook("PostCreateCrimenetContractGUI") -function MenuComponentManager._create_crimenet_contract_gui(self, node) - self.orig._create_crimenet_contract_gui(self, node) - Hooks:Call("PostCreateCrimenetContractGUI", self, node, self._crimenet_contract_gui) -end --- END OF FILE diff --git a/GoonBase/lua/MenuItemCustomizeController.lua b/GoonBase/lua/MenuItemCustomizeController.lua deleted file mode 100644 index ffdc5dd..0000000 --- a/GoonBase/lua/MenuItemCustomizeController.lua +++ /dev/null @@ -1,72 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( MenuItemCustomizeController ) - -function MenuItemCustomizeController.init(self, data_node, parameters) - CoreMenuItem.Item.init(self, data_node, parameters) - self._type = MenuItemCustomizeController.TYPE -end - -function MenuItemCustomizeController.setup_gui(self, node, row_item) - row_item.gui_panel = node.item_panel:panel({ - w = node.item_panel:w() - }) - row_item.controller_name = node._text_item_part(node, row_item, row_item.gui_panel, node._left_align(node)) - row_item.controller_name:set_align("right") - row_item.controller_binding = node._text_item_part(node, row_item, row_item.gui_panel, node._left_align(node), "left") - row_item.controller_binding:set_align("left") - row_item.controller_binding:set_text(string.upper(row_item.item:parameters().binding)) - row_item.controller_binding:set_color(tweak_data.menu.default_changeable_text_color) - self:_layout(node, row_item) - return true -end - -function MenuItemCustomizeController.reload(self, row_item, node) - if row_item ~= nil then - if self:parameters().axis then - row_item.controller_binding:set_text(string.upper(self:parameters().binding)) - else - row_item.controller_binding:set_text(string.upper(self:parameters().binding)) - end - end - return true -end - -function MenuItemCustomizeController.highlight_row_item(self, node, row_item, mouse_over) - if row_item ~= nil then - row_item.controller_binding:set_color(row_item.color) - row_item.controller_binding:set_font(row_item.font and Idstring(row_item.font) or tweak_data.menu.default_font_no_outline_id) - row_item.controller_name:set_color(row_item.color) - row_item.controller_name:set_font(row_item.font and Idstring(row_item.font) or tweak_data.menu.default_font_no_outline_id) - end - return true -end - -function MenuItemCustomizeController.fade_row_item(self, node, row_item) - if row_item ~= nil then - row_item.controller_name:set_color(row_item.color) - row_item.controller_name:set_font(row_item.font and Idstring(row_item.font) or tweak_data.menu.default_font_id) - row_item.controller_binding:set_color(tweak_data.menu.default_changeable_text_color) - row_item.controller_binding:set_font(row_item.font and Idstring(row_item.font) or tweak_data.menu.default_font_id) - end - return true -end - -function MenuItemCustomizeController._layout(self, node, row_item) - local safe_rect = managers.gui_data:scaled_size() - if row_item ~= nil then - row_item.controller_name:set_font_size(tweak_data.menu.customize_controller_size) - local x, y, w, h = row_item.controller_name:text_rect() - row_item.controller_name:set_height(h) - row_item.controller_name:set_right(row_item.gui_panel:w() - node.align_line_padding(node)) - row_item.gui_panel:set_height(h) - row_item.controller_binding:set_font_size(tweak_data.menu.customize_controller_size) - row_item.controller_binding:set_height(h) - row_item.controller_binding:set_left(node._right_align(node)) - end -end - --- END OF FILE diff --git a/GoonBase/lua/MenuManager.lua b/GoonBase/lua/MenuManager.lua deleted file mode 100644 index 05e5d73..0000000 --- a/GoonBase/lua/MenuManager.lua +++ /dev/null @@ -1,164 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( MenuManager ) -CloneClass( MenuCallbackHandler ) - -Hooks:RegisterHook( "MenuManagerInitialize" ) -function MenuManager.init(this, ...) - this.orig.init(this, ...) - Hooks:Call( "MenuManagerInitialize", this ) -end - -Hooks:RegisterHook( "MenuManagerOnOpenMenu" ) -function MenuManager.open_menu(this, menu_name, position) - this.orig.open_menu(this, menu_name, position) - Hooks:Call( "MenuManagerOnOpenMenu", this, menu_name, position ) -end - -function MenuManager.open_node(self, node_name, parameter_list) - self.orig.open_node(self, node_name, parameter_list) -end - -Hooks:RegisterHook( "MenuManagerSetMouseSensitivity" ) -function MenuManager.set_mouse_sensitivity(self, zoomed) - self.orig.set_mouse_sensitivity(self, zoomed) - Hooks:Call( "MenuManagerSetMouseSensitivity", self, zoomed ) -end - --- Add GoonBase to options -Hooks:RegisterHook( "MenuManagerSetupCustomMenus" ) -Hooks:RegisterHook( "MenuManagerPopulateCustomMenus" ) -Hooks:RegisterHook( "MenuManagerBuildCustomMenus" ) -Hooks:Add( "MenuManagerInitialize", "MenuManagerInitialize_AddGoonBaseMenuItem", function( menu_manager ) - - local success, err = pcall(function() - - MenuManager._goonbase_process_main_menu( menu_manager ) - MenuManager._goonbase_process_pause_menu( menu_manager ) - - end) - if not success then Print("[Error] " .. err) end - -end ) - -function MenuManager._goonbase_process_main_menu( menu_manager ) - - local psuccess, perror = pcall(function() - - local main_menu = menu_manager._registered_menus.menu_main - if main_menu == nil then return end - local mainmenu_nodes = main_menu.logic._data._nodes - - GoonBase.MenuHelper:SetupMenu( mainmenu_nodes, "video" ) - GoonBase.MenuHelper:SetupMenuButton( mainmenu_nodes, "options" ) - - Hooks:Call("MenuManagerSetupCustomMenus", menu_manager, mainmenu_nodes) - Hooks:Call("MenuManagerPopulateCustomMenus", menu_manager, mainmenu_nodes) - Hooks:Call("MenuManagerBuildCustomMenus", menu_manager, mainmenu_nodes) - - end) - if not psuccess then - Print("[Error] " .. perror) - end - -end - -function MenuManager._goonbase_process_pause_menu( menu_manager ) - - local psuccess, perror = pcall(function() - - local pause_menu = menu_manager._registered_menus.menu_pause - if pause_menu == nil then return end - local pausemenu_nodes = pause_menu.logic._data._nodes - - GoonBase.MenuHelper:SetupMenu( pausemenu_nodes, "video" ) - GoonBase.MenuHelper:SetupMenuButton( pausemenu_nodes, "options" ) - - Hooks:Call("MenuManagerSetupCustomMenus", menu_manager, pausemenu_nodes) - Hooks:Call("MenuManagerPopulateCustomMenus", menu_manager, pausemenu_nodes) - Hooks:Call("MenuManagerBuildCustomMenus", menu_manager, pausemenu_nodes) - - end) - if not psuccess then - Print("[Error] " .. perror) - end - -end - --- Start game delaying -Hooks:RegisterHook("MenuCallbackHandlerPreStartTheGame") -function MenuCallbackHandler.start_the_game(self) - - if not self._start_the_game then - self._start_the_game = function() - self.orig.start_the_game() - end - end - - local r = Hooks:ReturnCall("MenuCallbackHandlerPreStartTheGame", self) - if r then - self._delayed_start_game = true - return nil - end - - self.orig.start_the_game(self) -end - -function MenuCallbackHandler:_process_start_game_delay( t, dt ) - - if self._delayed_start_game and #self._start_delays == 0 then - self._delayed_start_game = false - if self._start_the_game then - self:_start_the_game() - end - end - -end - -function MenuCallbackHandler:delay_game_start( id ) - - self._delayed_start_game = true - - if not self._start_delays then - self._start_delays = {} - end - table.insert( self._start_delays, id ) - -end - -function MenuCallbackHandler:release_game_start_delay( id ) - - for i = #self._start_delays, 0, -1 do - if self._start_delays[i] == id then - table.remove( self._start_delays, i ) - end - end - -end - -Hooks:Add("MenuUpdate", "MenuUpdate_MenuManager", function(t, dt) - MenuCallbackHandler._process_start_game_delay(MenuCallbackHandler, t, dt) -end) - --- Lobby permissions -Hooks:RegisterHook("MenuCallbackHandlerPreChoseLobbyPermission") -function MenuCallbackHandler.choice_lobby_permission(self, item) - local r = Hooks:ReturnCall("MenuCallbackHandlerPreChoseLobbyPermission", self, item) - if r then - return - end - self.orig.choice_lobby_permission(self, item) -end - -Hooks:RegisterHook("MenuCallbackHandlerPreCrimeNetChoseLobbyPermission") -function MenuCallbackHandler.choice_crimenet_lobby_permission(self, item) - local r = Hooks:ReturnCall("MenuCallbackHandlerPreCrimeNetChoseLobbyPermission", self, item) - if r then - return - end - self.orig.choice_crimenet_lobby_permission(self, item) -end --- END OF FILE diff --git a/GoonBase/lua/MenuNodeGUI.lua b/GoonBase/lua/MenuNodeGUI.lua deleted file mode 100644 index 2a43305..0000000 --- a/GoonBase/lua/MenuNodeGUI.lua +++ /dev/null @@ -1,116 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( MenuNodeGui ) - -Hooks:RegisterHook("CustomizeControllerOnKeySet") -function MenuNodeGui._key_press(self, o, key, input_id, item, no_add) - - if managers.system_menu:is_active() then - return - end - if self._skip_first_mouse_0 then - self._skip_first_mouse_0 = false - if input_id == "mouse" and key == Idstring("0") then - return - end - end - local row_item = self:row_item(item) - if key == Idstring("esc") then - self:_end_customize_controller(o, item) - return - end - if input_id ~= "mouse" or not Input:mouse():button_name_str(key) then - end - local key_name = "" .. Input:keyboard():button_name_str(key) - if not no_add and input_id == "mouse" then - key_name = "mouse " .. key_name or key_name - end - local forbidden_btns = { - "esc", - "tab", - "num abnt c1", - "num abnt c2", - "@", - "ax", - "convert", - "kana", - "kanji", - "no convert", - "oem 102", - "stop", - "unlabeled", - "yen", - "mouse 8", - "mouse 9", - "" - } - for _, btn in ipairs(forbidden_btns) do - if Idstring(btn) == key then - managers.menu:show_key_binding_forbidden({KEY = key_name}) - self:_end_customize_controller(o, item) - return - end - end - local connections = managers.controller:get_settings(managers.controller:get_default_wrapper_type()):get_connection_map() - for _, name in ipairs(MenuCustomizeControllerCreator.CONTROLS) do - local connection = connections[name] - if connection._btn_connections then - for name, btn_connection in pairs(connection._btn_connections) do - if btn_connection.name == key_name and item:parameters().binding ~= btn_connection.name then - managers.menu:show_key_binding_collision({ - KEY = key_name, - MAPPED = managers.localization:text(MenuCustomizeControllerCreator.CONTROLS_INFO[name].text_id) - }) - self:_end_customize_controller(o, item) - return - end - end - else - for _, b_name in ipairs(connection:get_input_name_list()) do - if tostring(b_name) == key_name and item:parameters().binding ~= b_name then - managers.menu:show_key_binding_collision({ - KEY = key_name, - MAPPED = managers.localization:text(MenuCustomizeControllerCreator.CONTROLS_INFO[name].text_id) - }) - self:_end_customize_controller(o, item) - return - end - end - end - end - - if item:parameters().axis then - connections[item:parameters().axis]._btn_connections[item:parameters().button].name = key_name - managers.controller:set_user_mod(item:parameters().connection_name, { - axis = item:parameters().axis, - button = item:parameters().button, - connection = key_name - }) - item:parameters().binding = key_name - Hooks:Call("CustomizeControllerOnKeySet", connections[item:parameters().axis]) - else - if connections[item:parameters().button] == nil then - for k, v in pairs( connections ) do - connections[item:parameters().button] = clone(v) - break - end - connections[item:parameters().button]._name = item:parameters().connection_name - end - connections[item:parameters().button]:set_controller_id(input_id) - connections[item:parameters().button]:set_input_name_list({key_name}) - managers.controller:set_user_mod(item:parameters().connection_name, { - button = item:parameters().button, - connection = key_name, - controller_id = input_id - }) - item:parameters().binding = key_name - Hooks:Call("CustomizeControllerOnKeySet", connections[item:parameters().button]) - end - managers.controller:rebind_connections() - self:_end_customize_controller(o, item) - -end --- END OF FILE diff --git a/GoonBase/lua/MenuSceneManager.lua b/GoonBase/lua/MenuSceneManager.lua deleted file mode 100644 index 8e89b05..0000000 --- a/GoonBase/lua/MenuSceneManager.lua +++ /dev/null @@ -1,23 +0,0 @@ ----------- --- Payday 2 GoonMod, Public Release Beta 2, built on 1/4/2015 2:00:55 AM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( MenuSceneManager ) - -Hooks:RegisterHook("MenuSceneManagerSpawnedItemWeapon") -function MenuSceneManager.spawn_item_weapon(self, factory_id, blueprint, texture_switches) - local unit = self.orig.spawn_item_weapon(self, factory_id, blueprint, texture_switches) - Hooks:Call("MenuSceneManagerSpawnedItemWeapon", factory_id, blueprint, texture_switches, unit) - return unit -end - -Hooks:RegisterHook("MenuSceneManagerOverrideSceneTemplate") -function MenuSceneManager.set_scene_template(self, template, data, custom_name, skip_transition) - local r = Hooks:ReturnCall("MenuSceneManagerOverrideSceneTemplate", self, template, data, custom_name, skip_transition) - if r then - template = r - end - self.orig.set_scene_template(self, template, data, custom_name, skip_transition) -end --- END OF FILE diff --git a/GoonBase/lua/MenuSetup.lua b/GoonBase/lua/MenuSetup.lua deleted file mode 100644 index 1c1be99..0000000 --- a/GoonBase/lua/MenuSetup.lua +++ /dev/null @@ -1,19 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( MenuSetup ) - -Hooks:RegisterHook("MenuUpdate") -function MenuSetup.update(self, t, dt) - self.orig.update(self, t, dt) - Hooks:Call("MenuUpdate", t, dt) -end - -Hooks:RegisterHook("SetupOnQuit") -function MenuSetup.quit(self) - Hooks:Call("SetupOnQuit", self) - return self.orig.quit(self) -end --- END OF FILE diff --git a/GoonBase/lua/MissionBriefingGUI.lua b/GoonBase/lua/MissionBriefingGUI.lua deleted file mode 100644 index c7e3a77..0000000 --- a/GoonBase/lua/MissionBriefingGUI.lua +++ /dev/null @@ -1,15 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( MissionBriefingGui ) - -Hooks:RegisterHook("MissionBriefingGUIPreInit") -Hooks:RegisterHook("MissionBriefingGUIPostInit") -function MissionBriefingGui.init(self, saferect_ws, fullrect_ws, node) - Hooks:Call( "MissionBriefingGUIPreInit", self, saferect_ws, fullrect_ws, node ) - self.orig.init(self, saferect_ws, fullrect_ws, node) - Hooks:Call( "MissionBriefingGUIPostInit", self, saferect_ws, fullrect_ws, node ) -end --- END OF FILE diff --git a/GoonBase/lua/NPCRaycastWeaponBase.lua b/GoonBase/lua/NPCRaycastWeaponBase.lua deleted file mode 100644 index e790fa6..0000000 --- a/GoonBase/lua/NPCRaycastWeaponBase.lua +++ /dev/null @@ -1,25 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( NPCRaycastWeaponBase ) - -Hooks:RegisterHook("NPCRaycastWeaponBaseInit") -function NPCRaycastWeaponBase.init(self, unit) - self.orig.init(self, unit) - Hooks:Call("NPCRaycastWeaponBaseInit", self, unit) -end ---[[ dunno if these exist for enemy weapons but whatever -Hooks:RegisterHook("NPCRaycastWeaponBaseUpdate") -function NPCRaycastWeaponBase.update(self, unit, t, dt) - Hooks:Call("NPCRaycastWeaponBaseUpdate", self, unit, t, dt) -end - -Hooks:RegisterHook("NPCRaycastWeaponBaseSetFactoryData") -function NPCRaycastWeaponBase.set_factory_data(self, factory_id) - self.orig.set_factory_data(self, factory_id) - Hooks:Call("NPCRaycastWeaponBaseSetFactoryData", self, factory_id) -end -]]-- --- END OF FILE diff --git a/GoonBase/lua/NarrativeTweakData.lua b/GoonBase/lua/NarrativeTweakData.lua deleted file mode 100644 index e8834c9..0000000 --- a/GoonBase/lua/NarrativeTweakData.lua +++ /dev/null @@ -1,13 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( NarrativeTweakData ) - -Hooks:RegisterHook("NarrativeTweakDataInit") -function NarrativeTweakData.init(self) - self.orig.init(self) - Hooks:Call("NarrativeTweakDataInit", self) -end --- END OF FILE diff --git a/GoonBase/lua/NetworkGame.lua b/GoonBase/lua/NetworkGame.lua deleted file mode 100644 index 0d6ad5c..0000000 --- a/GoonBase/lua/NetworkGame.lua +++ /dev/null @@ -1,26 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( NetworkGame ) - -Hooks:RegisterHook("NetworkGamePostLoad") -function NetworkGame.load(self, game_data) - Print("NetworkGame.load()") - self.orig.load(self, game_data) - Hooks:Call("NetworkGamePostLoad", self, game_data) -end - -function NetworkGame.on_network_started(self) - Print("NetworkGame.on_network_started()") - self.orig.on_network_started(self) -end - - -function NetworkGame.init(self) - Print("NetworkGame.init()") - self.orig.init(self) - Print("network manager: ", managers.network) -end --- END OF FILE diff --git a/GoonBase/lua/NetworkMatchMakingSteam.lua b/GoonBase/lua/NetworkMatchMakingSteam.lua deleted file mode 100644 index 4ae1b55..0000000 --- a/GoonBase/lua/NetworkMatchMakingSteam.lua +++ /dev/null @@ -1,22 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( NetworkMatchMakingSTEAM ) - -Hooks:RegisterHook("NetworkMatchmakingSetAttributes") -function NetworkMatchMakingSTEAM.set_attributes(self, settings) - self.orig.set_attributes(self, settings) - Hooks:Call("NetworkMatchmakingSetAttributes", self, settings) - if self.lobby_handler then - self.lobby_handler:set_lobby_data( self._lobby_attributes ) - end -end - -Hooks:RegisterHook("NetworkMatchmakingJoinOKServer") -function NetworkMatchMakingSTEAM.join_server(self, room_id, skip_showing_dialog) - Hooks:Call("NetworkMatchmakingJoinOKServer", self, room_id, skip_showing_dialog) - self.orig.join_server(self, room_id, skip_showing_dialog) -end --- END OF FILE diff --git a/GoonBase/lua/NewRaycastWeaponBase.lua b/GoonBase/lua/NewRaycastWeaponBase.lua deleted file mode 100644 index 49e51bb..0000000 --- a/GoonBase/lua/NewRaycastWeaponBase.lua +++ /dev/null @@ -1,30 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( NewRaycastWeaponBase ) - -Hooks:RegisterHook("NewRaycastWeaponBaseInit") -function NewRaycastWeaponBase.init(self, unit) - self.orig.init(self, unit) - Hooks:Call("NewRaycastWeaponBaseInit", self, unit) -end - -Hooks:RegisterHook("NewRaycastWeaponBaseUpdate") -function NewRaycastWeaponBase.update(self, unit, t, dt) - Hooks:Call("NewRaycastWeaponBaseUpdate", self, unit, t, dt) -end - -Hooks:RegisterHook("NewRaycastWeaponBaseSetFactoryData") -function NewRaycastWeaponBase.set_factory_data(self, factory_id) - self.orig.set_factory_data(self, factory_id) - Hooks:Call("NewRaycastWeaponBaseSetFactoryData", self, factory_id) -end - -Hooks:RegisterHook("NewRaycastWeaponBasePostAssemblyComplete") -function NewRaycastWeaponBase.clbk_assembly_complete(self, clbk, parts, blueprint) - self.orig.clbk_assembly_complete(self, clbk, parts, blueprint) - Hooks:Call("NewRaycastWeaponBasePostAssemblyComplete", self, clbk, parts, blueprint) -end --- END OF FILE diff --git a/GoonBase/lua/PlayerDamage.lua b/GoonBase/lua/PlayerDamage.lua deleted file mode 100644 index 63e9737..0000000 --- a/GoonBase/lua/PlayerDamage.lua +++ /dev/null @@ -1,25 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( PlayerDamage ) - -Hooks:RegisterHook( "PlayerDamageOnPostInit" ) -function PlayerDamage.init(this, unit) - this.orig.init(this, unit) - Hooks:Call("PlayerDamageOnPostInit", this, unit) -end - -Hooks:RegisterHook( "PlayerDamageOnRegenerated" ) -function PlayerDamage._regenerated(this, no_messiah) - this.orig._regenerated(this, no_messiah) - Hooks:Call("PlayerDamageOnRegenerated", this, no_messiah) -end - -Hooks:RegisterHook( "PlayerDamageOnDowned" ) -function PlayerDamage.on_downed(self) - self.orig.on_downed(self) - Hooks:Call("PlayerDamageOnDowned", self) -end --- END OF FILE diff --git a/GoonBase/lua/PlayerInventory.lua b/GoonBase/lua/PlayerInventory.lua deleted file mode 100644 index 9172447..0000000 --- a/GoonBase/lua/PlayerInventory.lua +++ /dev/null @@ -1,45 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( PlayerInventory ) - -function PlayerInventory.add_unit_by_factory_name(self, factory_name, equip, instant, blueprint, texture_switches) - self.orig.add_unit_by_factory_name(self, factory_name, equip, instant, blueprint, texture_switches) -end - -function PlayerInventory._place_selection(self, selection_index, is_equip) - self.orig._place_selection(self, selection_index, is_equip) - self:create_riot_shield(self._unit) -end - -function PlayerInventory.create_riot_shield(self, unit) - - local psuccess, perror = pcall(function() - - -- local parent_unit = self._unit:camera()._camera_unit - -- local align_name = Idstring("a_weapon_right") - -- local align_obj = parent_unit:get_object( Idstring("a_weapon_right") ) - - -- self._shield_unit = World:spawn_unit(Idstring("units/payday2/characters/ene_acc_shield_lights/ene_acc_shield_lights"), align_obj:position(), align_obj:rotation()) - -- self._shield_unit:set_enabled(true) - -- self._shield_unit:damage():run_sequence_simple("held_body") - -- parent_unit:link(align_name, self._shield_unit, self._shield_unit:orientation_object():name()) - - -- local body = self._unit:body("mover_blocker") - -- if body then - -- body:set_enabled(false) - -- else - -- Print("could not find body") - -- end - - end) - if not psuccess then - Print("[Error] " .. perror) - end - -end - - --- END OF FILE diff --git a/GoonBase/lua/PlayerManager.lua b/GoonBase/lua/PlayerManager.lua deleted file mode 100644 index d5e8cfb..0000000 --- a/GoonBase/lua/PlayerManager.lua +++ /dev/null @@ -1,22 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( PlayerManager ) - -function PlayerManager.verify_carry(self, peer_id, carry_id) - if self._force_verify_carry and self._force_verify_carry > 0 then - self._force_verify_carry = self._force_verify_carry - 1 - return true - end - return self.orig.verify_carry(self, peer_id, carry_id) -end - -function PlayerManager:force_verify_carry() - if not self._force_verify_carry then - self._force_verify_carry = 0 - end - self._force_verify_carry = self._force_verify_carry + 1 -end --- END OF FILE diff --git a/GoonBase/lua/PlayerStandard.lua b/GoonBase/lua/PlayerStandard.lua deleted file mode 100644 index 962b1a8..0000000 --- a/GoonBase/lua/PlayerStandard.lua +++ /dev/null @@ -1,34 +0,0 @@ ----------- --- Payday 2 GoonMod, Public Release Beta 2, built on 1/9/2015 9:30:33 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( PlayerStandard ) - -Hooks:RegisterHook("PlayerStandardCheckActionInteract") -function PlayerStandard._check_action_interact(self, t, input) - local r = Hooks:ReturnCall("PlayerStandardCheckActionInteract", self, t, input) - if r ~= nil then - return r - end - return self.orig._check_action_interact(self, t, input) -end - -Hooks:RegisterHook("PlayerStandardStartMaskUp") -function PlayerStandard._enter(self, enter_data) - self.orig._enter(self, enter_data) - Hooks:Call("PlayerStandardStartMaskUp", self, enter_data) -end - -Hooks:RegisterHook("PlayerStandardStartActionEquipWeapon") -function PlayerStandard._start_action_equip_weapon(self, t) - self.orig._start_action_equip_weapon(self, t) - Hooks:Call("PlayerStandardStartActionEquipWeapon", self, t) -end - -Hooks:RegisterHook("PlayerStandardChangingWeapon") -function PlayerStandard._changing_weapon(self) - Hooks:Call("PlayerStandardChangingWeapon", self) - return self.orig._changing_weapon(self) -end --- END OF FILE diff --git a/GoonBase/lua/PreplanningTweakData.lua b/GoonBase/lua/PreplanningTweakData.lua deleted file mode 100644 index a4e6ba3..0000000 --- a/GoonBase/lua/PreplanningTweakData.lua +++ /dev/null @@ -1,40 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( PrePlanningTweakData ) - -function PrePlanningTweakData._create_locations(this, tweak_data) - - this.orig._create_locations(this, tweak_data) - - this.locations.branchbank = { - default_plans = { - escape_plan = "escape_helicopter_loud", - vault_plan = "vault_big_drill" - }, - total_budget = 10, - start_location = { - group = "a", - x = 1500, - y = 1025, - zoom = 1.5 - }, - { - name_id = "menu_pp_big_loc_a", - texture = "guis/textures/pd2/mission_briefing/assets/bank/assets_bank_blueprint", - map_x = -1.1, - map_y = 0.5, - map_size = 1, - x1 = -250, - y1 = -3000, - x2 = 5750, - y2 = 3000, - rotation = 0, - custom_points = {} - } - } - -end --- END OF FILE diff --git a/GoonBase/lua/QuickSmokeGrenade.lua b/GoonBase/lua/QuickSmokeGrenade.lua deleted file mode 100644 index eaa84cc..0000000 --- a/GoonBase/lua/QuickSmokeGrenade.lua +++ /dev/null @@ -1,19 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( QuickSmokeGrenade ) - -Hooks:RegisterHook( "QuickSmokeGrenadeActivate" ) -function QuickSmokeGrenade.activate(this, pos, duration) - this.orig.activate(this, pos, duration) - Hooks:Call("QuickSmokeGrenadeActivate", this, pos, duration) -end - -Hooks:RegisterHook( "QuickSmokeGrenadeDetonate" ) -function QuickSmokeGrenade.detonate(this) - this.orig.detonate(this) - Hooks:Call("QuickSmokeGrenadeDetonate", this) -end --- END OF FILE diff --git a/GoonBase/lua/Setup.lua b/GoonBase/lua/Setup.lua deleted file mode 100644 index 650e49a..0000000 --- a/GoonBase/lua/Setup.lua +++ /dev/null @@ -1,13 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( Setup ) - -Hooks:RegisterHook("SetupOnQuit") -function Setup.quit(self) - Hooks:Call("SetupOnQuit", self) - return self.orig.quit(self) -end --- END OF FILE diff --git a/GoonBase/lua/SpoocLogicAttack.lua b/GoonBase/lua/SpoocLogicAttack.lua deleted file mode 100644 index fe93b30..0000000 --- a/GoonBase/lua/SpoocLogicAttack.lua +++ /dev/null @@ -1,47 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - - -function SpoocLogicAttack.action_complete_clbk(data, action) - local action_type = action:type() - local my_data = data.internal_data - if action_type == "walk" then - my_data.advancing = nil - if my_data.surprised then - my_data.surprised = false - elseif my_data.moving_to_cover then - if action:expired() then - my_data.in_cover = my_data.moving_to_cover - CopLogicAttack._set_nearest_cover(my_data, my_data.in_cover) - my_data.cover_enter_t = data.t - my_data.cover_sideways_chk = nil - end - my_data.moving_to_cover = nil - elseif my_data.walking_to_cover_shoot_pos then - my_data.walking_to_cover_shoot_pos = nil - end - elseif action_type == "shoot" then - my_data.shooting = nil - elseif action_type == "turn" then - my_data.turning = nil - elseif action_type == "spooc" then - Print("SPOOKED BY A SPOOKY SPOOK") - data.spooc_attack_timeout_t = TimerManager:game():time() + math.lerp(data.char_tweak.spooc_attack_timeout[1], data.char_tweak.spooc_attack_timeout[2], math.random()) - if action:complete() and data.char_tweak.spooc_attack_use_smoke_chance > 0 and math.random() <= data.char_tweak.spooc_attack_use_smoke_chance and not managers.groupai:state():is_smoke_grenade_active() then - managers.groupai:state():detonate_smoke_grenade(data.m_pos + math.UP * 10, data.unit:movement():m_head_pos(), math.lerp(15, 30, math.random()), false) - end - my_data.spooc_attack = nil - elseif action_type == "dodge" then - local timeout = action:timeout() - if timeout then - data.dodge_timeout_t = TimerManager:game():time() + math.lerp(timeout[1], timeout[2], math.random()) - end - CopLogicAttack._cancel_cover_pathing(data, my_data) - if action:expired() then - SpoocLogicAttack._upd_aim(data, my_data) - end - end -end --- END OF FILE diff --git a/GoonBase/lua/TweakData.lua b/GoonBase/lua/TweakData.lua deleted file mode 100644 index 816f71a..0000000 --- a/GoonBase/lua/TweakData.lua +++ /dev/null @@ -1,14 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -TweakData.orig = {} -TweakData.orig.init = TweakData.init - -Hooks:RegisterHook("TweakDataPostInit") -function TweakData.init(this) - this.orig.init(this) - Hooks:Call("TweakDataPostInit") -end --- END OF FILE diff --git a/GoonBase/lua/WeaponFlashlight.lua b/GoonBase/lua/WeaponFlashlight.lua deleted file mode 100644 index fd6c783..0000000 --- a/GoonBase/lua/WeaponFlashlight.lua +++ /dev/null @@ -1,51 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( WeaponFlashLight ) - -Hooks:RegisterHook("WeaponFlashLightInit") -function WeaponFlashLight.init(self, unit) - self.orig.init(self, unit) - Hooks:Call( "WeaponFlashLightInit", self, unit ) -end - -Hooks:RegisterHook("WeaponFlashLightCheckState") -function WeaponFlashLight._check_state(self) - self.orig._check_state(self) - Hooks:Call( "WeaponFlashLightCheckState", self ) -end - --- TODO: This is a messy hack-fix. Fix this up proper sometime. -function WeaponFlashLight:overkill_update(unit, t, dt) - - t = Application:time() - self._light_speed = self._light_speed or 1 - self._light_speed = math.step(self._light_speed, 1, dt * (math.random(4) + 2)) - -- self._light:set_rotation(self._light:rotation() * Rotation(dt * -50 * self._light_speed, 0)) - self:update_flicker(t, dt) - self:update_laughter(t, dt) - if not self._kittens_timer then - self._kittens_timer = t + 25 - end - if t > self._kittens_timer then - if math.rand(1) < 0.75 then - self:run_net_event(self.HALLOWEEN_FLICKER) - self._kittens_timer = t + math.random(10) + 5 - elseif math.rand(1) < 0.35 then - self:run_net_event(self.HALLOWEEN_WARP) - self._kittens_timer = t + math.random(12) + 3 - elseif math.rand(1) < 0.25 then - self:run_net_event(self.HALLOWEEN_LAUGHTER) - self._kittens_timer = t + math.random(5) + 8 - elseif math.rand(1) < 0.15 then - self:run_net_event(self.HALLOWEEN_SPOOC) - self._kittens_timer = t + math.random(2) + 3 - else - self._kittens_timer = t + math.random(5) + 3 - end - end - -end --- END OF FILE diff --git a/GoonBase/lua/WeaponLaser.lua b/GoonBase/lua/WeaponLaser.lua deleted file mode 100644 index 181c50a..0000000 --- a/GoonBase/lua/WeaponLaser.lua +++ /dev/null @@ -1,42 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( WeaponLaser ) - -Hooks:RegisterHook("WeaponLaserInit") -function WeaponLaser.init(self, unit) - self.orig.init(self, unit) - Hooks:Call("WeaponLaserInit", self, unit) -end - -Hooks:RegisterHook("WeaponLaserUpdate") -function WeaponLaser.update(self, unit, t, dt) - self.orig.update(self, unit, t, dt) - Hooks:Call("WeaponLaserUpdate", self, unit, t, dt) -end - -Hooks:RegisterHook("WeaponLaserSetNPC") -function WeaponLaser.set_npc(self) - self.orig.set_npc(self) - Hooks:Call("WeaponLaserSetNPC", self) -end - -Hooks:RegisterHook("WeaponLaserPostSetColorByTheme") -function WeaponLaser.set_color_by_theme(self, theme) - self.orig.set_color_by_theme(self, theme) - Hooks:Call("WeaponLaserPostSetColorByTheme", self, theme) -end - -Hooks:RegisterHook("WeaponLaserSetOn") -Hooks:RegisterHook("WeaponLaserSetOff") -function WeaponLaser._check_state(self) - self.orig._check_state(self) - if self._on then - Hooks:Call("WeaponLaserSetOn", self) - else - Hooks:Call("WeaponLaserSetOff", self) - end -end --- END OF FILE diff --git a/GoonBase/lua/WeaponTweakData.lua b/GoonBase/lua/WeaponTweakData.lua deleted file mode 100644 index d57bb1a..0000000 --- a/GoonBase/lua/WeaponTweakData.lua +++ /dev/null @@ -1,13 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -CloneClass( WeaponTweakData ) - -Hooks:RegisterHook("WeaponTweakDataInitNewWeapons") -function WeaponTweakData._init_new_weapons(self, ...) - self.orig._init_new_weapons(self, arg) - Hooks:Call("WeaponTweakDataInitNewWeapons") -end --- END OF FILE diff --git a/GoonBase/mods/body_count.lua b/GoonBase/mods/body_count.lua deleted file mode 100644 index ae80070..0000000 --- a/GoonBase/mods/body_count.lua +++ /dev/null @@ -1,288 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - --- Mod Definition -local Mod = class( BaseMod ) -Mod.id = "BodyCountMod" -Mod.Name = "Corpse Delimiter" -Mod.Desc = "Change the amount of bodies that can appear after enemies are killed" -Mod.Requirements = {} -Mod.Incompatibilities = {} -Mod.EnabledByDefault = true - -Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod:ID(), function() - GoonBase.Mods:RegisterMod( Mod ) -end) - -if not Mod:IsEnabled() then - return -end - --- Body Count Mod -_G.GoonBase.CorpseDelimiter = _G.GoonBase.CorpseDelimiter or {} -local CorpseDelimiter = _G.GoonBase.CorpseDelimiter -local body_count_menu_id = "goonbase_corpse_mod_menu" -CorpseDelimiter.RemoveAllBodiesKey = "BodyCountModRemoveAll" -CorpseDelimiter.RemoveAllShieldsKey = "BodyCountModRemoveShields" -CorpseDelimiter.CustomKeys = { - REMOVE_ALL = GoonBase.Options.BodyCount ~= nil and GoonBase.Options.BodyCount.RemoveAllBodiesKey or "", - REMOVE_SHIELDS = GoonBase.Options.BodyCount ~= nil and GoonBase.Options.BodyCount.RemoveAllShieldsKey or "" -} - --- Options -if GoonBase.Options.BodyCount == nil then - GoonBase.Options.BodyCount = {} - GoonBase.Options.BodyCount.CustomCorpseLimit = true - GoonBase.Options.BodyCount.MaxCorpses = 1024 - GoonBase.Options.BodyCount.CurrentMaxCorpses = 1024 - GoonBase.Options.BodyCount.RemoveShields = false - GoonBase.Options.BodyCount.ShieldTime = 60 - GoonBase.Options.BodyCount.MaxShieldTime = 600 - GoonBase.Options.BodyCount.RemoveAllBodiesKey = "" - GoonBase.Options.BodyCount.RemoveAllShieldsKey = "" -end - --- Localization -local Localization = GoonBase.Localization -Localization.OptionsMenu_CorpseSubmenuTitle = "Corpses" -Localization.OptionsMenu_CorpseSubmenuDesc = "Change settings for the ingame Corpses" -Localization.OptionsMenu_CorpseToggle = "Use Custom Corpse Amount" -Localization.OptionsMenu_CorpseToggleDesc = "Use the custom amount of corpses instead of the default amount (8)" -Localization.OptionsMenu_CorpseAmount = "Corpse Amount" -Localization.OptionsMenu_CorpseAmountDesc = "Maximum number of corpses allowed (Current: {1})" -Localization.OptionsMenu_CorpseShieldToggle = "Despawn Shields" -Localization.OptionsMenu_CorpseShieldToggleDesc = "Despawn shields after a short time" -Localization.OptionsMenu_CorpseShieldTimer = "Shield Despawn Time" -Localization.OptionsMenu_CorpseShieldTimerDesc = "Despawn shields after {1} seconds" -Localization.OptionsMenu_RemoveAllCorpses = "Remove All Corpses" -Localization.OptionsMenu_RemoveAllCorpsesDesc = "Key to clean up all corpses in the level when pressed" -Localization.OptionsMenu_RemoveAllShields = "Remove All Shields" -Localization.OptionsMenu_RemoveAllShieldsDesc = "Key to clean up all shields in the level when pressed" - -Localization.OptionsMenu_CorpseAmountDesc_Default = Localization.OptionsMenu_CorpseAmountDesc -Localization.OptionsMenu_CorpseShieldTimerDesc_Default = Localization.OptionsMenu_CorpseShieldTimerDesc - --- Stop bodies from despawning -Hooks:Add("EnemyManagerPreUpdateCorpseDisposal", "EnemyManagerPreUpdateCorpseDisposal_BodyCount", function(enemy_manager) - enemy_manager._MAX_NR_CORPSES = GoonBase.Options.BodyCount.CustomCorpseLimit and GoonBase.Options.BodyCount.CurrentMaxCorpses or 8 -end) - --- Despawn shields after time -local shield_timer_id = 0 -Hooks:Add("CopInventoryDropShield", "CopInventoryDropShield_BodyCount", function(inventory) - - if GoonBase.Options.BodyCount.CustomCorpseLimit and GoonBase.Options.BodyCount.RemoveShields then - - local id = "CopInventoryDropShield_" .. tostring(shield_timer_id) - shield_timer_id = shield_timer_id + 1 - - Queue:Add(id, function() - if inventory ~= nil then - inventory:destroy_all_items() - end - end, GoonBase.Options.BodyCount.ShieldTime) - - end - -end) - --- Menu -Hooks:Add("MenuManagerSetupCustomMenus", "MenuManagerSetupCustomMenus_BodyCount", function(menu_manager, menu_nodes) - GoonBase.MenuHelper:NewMenu( body_count_menu_id ) -end) - -Hooks:Add("MenuManagerSetupGoonBaseMenu", "MenuManagerSetupGoonBaseMenu_BodyCount", function( menu_manager ) - - -- Add corpse mod menu button - GoonBase.MenuHelper:AddButton({ - id = "corpse_mod_menu_button", - title = "OptionsMenu_CorpseSubmenuTitle", - desc = "OptionsMenu_CorpseSubmenuDesc", - next_node = body_count_menu_id, - menu_id = "goonbase_options_menu" - }) - - -- Callbacks - MenuCallbackHandler.toggle_corpse = function(this, item) - GoonBase.Options.BodyCount.CustomCorpseLimit = item:value() == "on" and true or false - GoonBase.Options:Save() - end - - MenuCallbackHandler.set_corpse_amount = function(this, item) - GoonBase.Options.BodyCount.CurrentMaxCorpses = math.floor( item:value() ) - GoonBase.Localization.OptionsMenu_CorpseAmountDesc = GoonBase.Localization.OptionsMenu_CorpseAmountDesc_Default:gsub("{1}", GoonBase.Options.BodyCount.CurrentMaxCorpses) - GoonBase.Options:Save() - end - - MenuCallbackHandler.toggle_shield_timer = function(this, item) - GoonBase.Options.BodyCount.RemoveShields = item:value() == "on" and true or false - GoonBase.Options:Save() - end - - MenuCallbackHandler.set_shield_timer = function(this, item) - GoonBase.Options.BodyCount.ShieldTime = math.floor( item:value() ) - GoonBase.Localization.OptionsMenu_CorpseShieldTimerDesc = GoonBase.Localization.OptionsMenu_CorpseShieldTimerDesc_Default:gsub("{1}", GoonBase.Options.BodyCount.ShieldTime) - GoonBase.Options:Save() - end - - -- Localization - GoonBase.Localization.OptionsMenu_CorpseAmountDesc = GoonBase.Localization.OptionsMenu_CorpseAmountDesc_Default:gsub("{1}", GoonBase.Options.BodyCount.CurrentMaxCorpses) - GoonBase.Localization.OptionsMenu_CorpseShieldTimerDesc = GoonBase.Localization.OptionsMenu_CorpseShieldTimerDesc_Default:gsub("{1}", GoonBase.Options.BodyCount.ShieldTime) - - -- Custom Corpse Amount Toggle - GoonBase.MenuHelper:AddToggle({ - id = "toggle_custom_corpse", - title = "OptionsMenu_CorpseToggle", - desc = "OptionsMenu_CorpseToggleDesc", - callback = "toggle_corpse", - value = GoonBase.Options.BodyCount.CustomCorpseLimit, - menu_id = body_count_menu_id, - priority = 50 - }) - - -- Corpse Amount Slider - GoonBase.MenuHelper:AddSlider({ - id = "corpse_amount_slider", - title = "OptionsMenu_CorpseAmount", - desc = "OptionsMenu_CorpseAmountDesc", - callback = "set_corpse_amount", - value = GoonBase.Options.BodyCount.CurrentMaxCorpses, - min = 8, - max = GoonBase.Options.BodyCount.MaxCorpses, - step = 1, - show_value = true, - menu_id = body_count_menu_id, - priority = 49 - }) - - -- Despawn Shields - GoonBase.MenuHelper:AddToggle({ - id = "toggle_shield_timer", - title = "OptionsMenu_CorpseShieldToggle", - desc = "OptionsMenu_CorpseShieldToggleDesc", - callback = "toggle_shield_timer", - value = GoonBase.Options.BodyCount.RemoveShields, - menu_id = body_count_menu_id, - priority = 48 - }) - - -- Shield Timer Slider - GoonBase.MenuHelper:AddSlider({ - id = "shield_timer_slider", - title = "OptionsMenu_CorpseShieldTimer", - desc = "OptionsMenu_CorpseShieldTimerDesc", - callback = "set_shield_timer", - value = GoonBase.Options.BodyCount.ShieldTime, - min = 15, - max = GoonBase.Options.BodyCount.MaxShieldTime, - step = 1, - show_value = true, - menu_id = body_count_menu_id, - priority = 47 - }) - - -- Remove bindings - GoonBase.MenuHelper:AddDivider({ - id = "keybind_corpse_menu_divider", - menu_id = body_count_menu_id, - size = 16, - priority = 30, - }) - - GoonBase.MenuHelper:AddKeybinding({ - id = "keybind_corpse_remove_all", - title = managers.localization:text("OptionsMenu_RemoveAllCorpses"), - desc = managers.localization:text("OptionsMenu_RemoveAllCorpsesDesc"), - connection_name = CorpseDelimiter.RemoveAllBodiesKey, - button = CorpseDelimiter.CustomKeys.REMOVE_ALL, - binding = CorpseDelimiter.CustomKeys.REMOVE_ALL, - menu_id = body_count_menu_id, - priority = 20 - }) - - GoonBase.MenuHelper:AddKeybinding({ - id = "keybind_corpse_remove_shields", - title = managers.localization:text("OptionsMenu_RemoveAllShields"), - desc = managers.localization:text("OptionsMenu_RemoveAllShieldsDesc"), - connection_name = CorpseDelimiter.RemoveAllShieldsKey, - button = CorpseDelimiter.CustomKeys.REMOVE_SHIELDS, - binding = CorpseDelimiter.CustomKeys.REMOVE_SHIELDS, - menu_id = body_count_menu_id, - priority = 19 - }) - -end) - -Hooks:Add("MenuManagerBuildCustomMenus", "MenuManagerBuildCustomMenus_BodyCount", function(menu_manager, mainmenu_nodes) - mainmenu_nodes[body_count_menu_id] = GoonBase.MenuHelper:BuildMenu( body_count_menu_id ) -end) - --- Custom keybinds -Hooks:Add("CustomizeControllerOnKeySet", "CustomizeControllerOnKeySet_" .. Mod:ID(), function(item) - - local psuccess, perror = pcall(function() - - if item._name == CorpseDelimiter.RemoveAllBodiesKey then - CorpseDelimiter.CustomKeys.REMOVE_ALL = item._input_name_list[1] - CorpseDelimiter:SaveBindings() - end - - if item._name == CorpseDelimiter.RemoveAllShieldsKey then - CorpseDelimiter.CustomKeys.REMOVE_SHIELDS = item._input_name_list[1] - CorpseDelimiter:SaveBindings() - end - - end) - if not psuccess then - Print("[Error] " .. perror) - end - -end) - -function CorpseDelimiter:SaveBindings() - GoonBase.Options.BodyCount.RemoveAllBodiesKey = CorpseDelimiter.CustomKeys.REMOVE_ALL - GoonBase.Options.BodyCount.RemoveAllShieldsKey = CorpseDelimiter.CustomKeys.REMOVE_SHIELDS - GoonBase.Options:Save() -end - -Hooks:Add("GameSetupUpdate", "GameSetupUpdate_" .. Mod:ID(), function(t, dt) - CorpseDelimiter:UpdateBindings() -end) - -function CorpseDelimiter:UpdateBindings() - - if self._input == nil then - self._input = Input:keyboard() - end - if managers.hud:chat_focus() then - return - end - - local remove_all_key = CorpseDelimiter.CustomKeys.REMOVE_ALL - if not string.is_nil_or_empty(remove_all_key) and self._input:pressed(Idstring(remove_all_key)) then - if not managers.groupai:state():whisper_mode() then - managers.enemy:dispose_all_corpses() - end - end - - local remove_shields_key = CorpseDelimiter.CustomKeys.REMOVE_SHIELDS - if not string.is_nil_or_empty(remove_shields_key) and self._input:pressed(Idstring(remove_shields_key)) then - CorpseDelimiter:RemoveAllShields() - end - -end - -function CorpseDelimiter:RemoveAllShields() - - local enemy_data = managers.enemy._enemy_data - local corpses = enemy_data.corpses - for u_key, u_data in pairs(corpses) do - if u_data.unit:inventory() ~= nil then - u_data.unit:inventory():destroy_all_items() - end - end - -end --- END OF FILE diff --git a/GoonBase/mods/colors/color_hsvrgb.lua b/GoonBase/mods/colors/color_hsvrgb.lua deleted file mode 100644 index d287ba7..0000000 --- a/GoonBase/mods/colors/color_hsvrgb.lua +++ /dev/null @@ -1,219 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -ColorHSVRGB = class() - -function ColorHSVRGB:init() - self._ID = "DefaultID" - self._menu_id = nil - self._using_hsv = false - self._priority = 0 - self._r = 0 - self._g = 0 - self._b = 0 -end - -function ColorHSVRGB:ID() - return self._ID -end - -function ColorHSVRGB:UsingHSV() - return self._using_hsv -end - -function ColorHSVRGB:GetColor(alpha) - - local r, g, b = 0, 0, 0 - if self:UsingHSV() then - r, g, b = self:ToRGB(self._r, self._g, self._b) - else - r = self._r - g = self._g - b = self._b - end - - return Color(alpha or 1, r, g, b) - -end - -function ColorHSVRGB:ToRGB(h, s, v) - - local r, g, b - - local i = math.floor(h * 6) - local f = h * 6 - i - local p = v * (1 - s) - local q = v * (1 - f * s) - local t = v * (1 - (1 - f) * s) - - local mod = i % 6 - if mod == 0 then - r = v - g = t - b = p - elseif mod == 1 then - r = q - g = v - b = p - elseif mod == 2 then - r = p - g = v - b = t - elseif mod == 3 then - r = p - g = q - b = v - elseif mod == 4 then - r = t - g = p - b = v - elseif mod == 5 then - r = v - g = p - b = q - end - - return r, g, b - -end - -function ColorHSVRGB:SetID(id) - self._ID = id -end - -function ColorHSVRGB:SetHSV(hsv) - self._using_hsv = hsv -end - -function ColorHSVRGB:SetOptionsTable(tbl) - - self.options_table = tbl - - self._r = GoonBase.Options[tbl].R - self._g = GoonBase.Options[tbl].G - self._b = GoonBase.Options[tbl].B - self._using_hsv = GoonBase.Options[tbl].HSV - -end - -function ColorHSVRGB:SetupLocalization() - - local id = self:ID() - local hsv = self:UsingHSV() - local Localization = GoonBase.Localization - - Localization["Options_ToggleColorHSV_Title"] = "Use HSV" - Localization["Options_ToggleColorHSV_Message"] = "Use HSV instead of RGB" - - Localization["Options_" .. id .. "_RH_Title"] = hsv and "Hue/Red" or "Red/Hue" - Localization["Options_" .. id .. "_GS_Title"] = hsv and "Saturation/Green" or "Green/Saturation" - Localization["Options_" .. id .. "_BV_Title"] = hsv and "Value/Blue" or "Blue/Value" - Localization["Options_" .. id .. "_Example"] = "Color Example" - - Localization["Options_" .. id .. "_RH_Desc"] = string.gsub( "Control the {1} of the colour", "{1}", hsv and "Hue" or "Red" ) - Localization["Options_" .. id .. "_GS_Desc"] = string.gsub( "Control the {1} of the colour", "{1}", hsv and "Saturation" or "Green" ) - Localization["Options_" .. id .. "_BV_Desc"] = string.gsub( "Control the {1} of the colour", "{1}", hsv and "Value" or "Blue" ) - Localization["Options_" .. id .. "_ExampleMessage"] = "Re-open this menu to update the color example" - -end - -function ColorHSVRGB:SetPriority( prio ) - self._priority = prio -end - -function ColorHSVRGB:SetupMenu( menu_id ) - - local id = self:ID() - local r = GoonBase.Options[self.options_table].R - local g = GoonBase.Options[self.options_table].G - local b = GoonBase.Options[self.options_table].B - local hsv = GoonBase.Options[self.options_table].HSV - - self:SetupLocalization() - self._menu_id = menu_id - - MenuCallbackHandler["set_rh_" .. id] = function(this, item) - self._r = item:value() - GoonBase.Options[self.options_table].R = self._r - GoonBase.Options:Save() - end - - MenuCallbackHandler["set_gs_" .. id] = function(this, item) - self._g = item:value() - GoonBase.Options[self.options_table].G = self._g - GoonBase.Options:Save() - end - - MenuCallbackHandler["set_bv_" .. id] = function(this, item) - self._b = item:value() - GoonBase.Options[self.options_table].B = self._b - GoonBase.Options:Save() - end - - MenuCallbackHandler["toggle_hsv_" .. id] = function(this, item) - - local enabled = item:value() == "on" and true or false - self:SetHSV( enabled ) - GoonBase.Options[self.options_table].HSV = enabled - GoonBase.Options:Save() - - self:SetupLocalization() - - end - - GoonBase.MenuHelper:AddSlider({ - id = "slider_rh_" .. id, - title = "Options_" .. id .. "_RH_Title", - desc = "Options_" .. id .. "_RH_Desc", - callback = "set_rh_" .. id, - value = r, - min = 0, - max = 1, - step = 0.01, - show_value = true, - menu_id = menu_id, - priority = self._priority + 5, - }) - - GoonBase.MenuHelper:AddSlider({ - id = "slider_gs_" .. id, - title = "Options_" .. id .. "_GS_Title", - desc = "Options_" .. id .. "_GS_Desc", - callback = "set_gs_" .. id, - value = g, - min = 0, - max = 1, - step = 0.01, - show_value = true, - menu_id = menu_id, - priority = self._priority + 4, - }) - - GoonBase.MenuHelper:AddSlider({ - id = "slider_bv_" .. id, - title = "Options_" .. id .. "_BV_Title", - desc = "Options_" .. id .. "_BV_Desc", - callback = "set_bv_" .. id, - value = b, - min = 0, - max = 1, - step = 0.01, - show_value = true, - menu_id = menu_id, - priority = self._priority + 3, - }) - - GoonBase.MenuHelper:AddToggle({ - id = "toggle_hsv_" .. id, - title = "Options_ToggleColorHSV_Title", - desc = "Options_ToggleColorHSV_Message", - callback = "toggle_hsv_" .. id, - value = hsv, - menu_id = menu_id, - priority = self._priority, - }) - -end --- END OF FILE diff --git a/GoonBase/mods/colors/enemy_weapon_laser.lua b/GoonBase/mods/colors/enemy_weapon_laser.lua deleted file mode 100644 index 71d0ddc..0000000 --- a/GoonBase/mods/colors/enemy_weapon_laser.lua +++ /dev/null @@ -1,252 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - --- Mod Definition -local Mod = class( BaseMod ) -Mod.id = "CustomEnemyWeaponLaser" -Mod.Name = "Custom Enemy Laser Colour" -Mod.Desc = "Set a custom colour for lasers attached to enemy weapons" -Mod.Requirements = {} -Mod.Incompatibilities = {} -Mod.Priority = 1 - -Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() - GoonBase.Mods:RegisterMod( Mod ) -end) - -if not Mod:IsEnabled() then - return -end - --- Lasers -GoonBase.EnemyLaser = GoonBase.EnemyLaser or {} -local Laser = GoonBase.EnemyLaser -Laser.MenuId = "goonbase_enemy_laser_menu" -Laser.Color = nil - --- Localization -local Localization = GoonBase.Localization -Localization.Options_EnemyLaserName = "Enemy Laser Color" -Localization.Options_EnemyLaserDesc = "Modify the color of enemy's weapons lasers" -Localization.Options_EnemyLaserEnableTitle = "Enable Custom Enemy Laser Color" -Localization.Options_EnemyLaserEnableDesc = "Use the custom set color for enemy's weapons Lasers" -Localization.Options_EnemyLaserRainbowTitle = "Enable Rainbow Laser" -Localization.Options_EnemyLaserRainbowDesc = "Enable rainbow instead of the set Hue" -Localization.Options_EnemyLaserRainbowSpeedTitle = "Rainbow Speed" -Localization.Options_EnemyLaserRainbowSpeedDesc = "Set the speed of the rainbow effect" - --- Options -if GoonBase.Options.EnemyLaser == nil then - GoonBase.Options.EnemyLaser = {} - GoonBase.Options.EnemyLaser.Enabled = false - GoonBase.Options.EnemyLaser.R = 1 - GoonBase.Options.EnemyLaser.G = 0.1 - GoonBase.Options.EnemyLaser.B = 0.1 - GoonBase.Options.EnemyLaser.HSV = false - GoonBase.Options.EnemyLaser.Rainbow = false - GoonBase.Options.EnemyLaser.RainbowSpeed = 10 -end - --- Functions -function Laser:IsEnabled() - return GoonBase.Options.EnemyLaser.Enabled -end - -function Laser:IsRainbow() - return GoonBase.Options.EnemyLaser.Rainbow -end - -function Laser:GetColor(alpha) - if Laser.Color == nil then - Laser.Color = ColorHSVRGB:new() - Laser.Color:SetOptionsTable( "EnemyLaser" ) - end - return Laser.Color:GetColor( alpha ) or Color( alpha or 1, 1, 0, 0 ) -end - -function Laser:IsNPCPlayerUnitLaser( laser ) - - if not self._laser_units_lookup then - self._laser_units_lookup = {} - end - - local laser_key = nil - if laser._unit then - laser_key = laser._unit:key() - end - if laser_key and self._laser_units_lookup[laser_key] ~= nil then - return self._laser_units_lookup[laser_key] - end - - local criminals_manager = managers.criminals - if not criminals_manager then - return - end - - for id, data in pairs(criminals_manager._characters) do - if data.unit ~= nil and alive(data.unit) and data.name ~= criminals_manager:local_character_name() then - - if data.unit:inventory() and data.unit:inventory():equipped_unit() then - - local wep_base = data.unit:inventory():equipped_unit():base() - if wep_base then - - if wep_base._factory_id ~= nil and wep_base._blueprint ~= nil then - - local gadgets = managers.weapon_factory:get_parts_from_weapon_by_type_or_perk("gadget", wep_base._factory_id, wep_base._blueprint) - if gadgets then - local gadget - for _, i in ipairs(gadgets) do - - gadget = wep_base._parts[i] - gadget = gadget.unit:base() - - if gadget == laser then - if laser_key then - self._laser_units_lookup[laser_key] = true - end - return true - end - - end - end - - end - - end - - end - - end - end - - if laser_key then - self._laser_units_lookup[laser_key] = false - end - return false - -end - --- Menu -Hooks:Add("MenuManagerSetupCustomMenus", "MenuManagerSetupCustomMenus_EnemyLaser", function(menu_manager, menu_nodes) - GoonBase.MenuHelper:NewMenu( Laser.MenuId ) -end) - -Hooks:Add("MenuManagerSetupGoonBaseMenu", "MenuManagerSetupGoonBaseMenu_EnemyLaser", function(menu_manager, menu_nodes) - - -- Submenu Button - GoonBase.MenuHelper:AddButton({ - id = "enemy_laser_button", - title = "Options_EnemyLaserName", - desc = "Options_EnemyLaserDesc", - next_node = Laser.MenuId, - menu_id = "goonbase_options_menu" - }) - - -- Enabled Toggle - MenuCallbackHandler.toggle_custom_enemy_laser_color = function(this, item) - GoonBase.Options.EnemyLaser.Enabled = item:value() == "on" and true or false - GoonBase.Options:Save() - end - - GoonBase.MenuHelper:AddToggle({ - id = "toggle_custom_enemy_laser_color", - title = "Options_EnemyLaserEnableTitle", - desc = "Options_EnemyLaserEnableDesc", - callback = "toggle_custom_enemy_laser_color", - value = GoonBase.Options.EnemyLaser.Enabled, - menu_id = Laser.MenuId, - priority = 11 - }) - - -- RGB/HSV Colour - Laser.Color = ColorHSVRGB:new() - Laser.Color:SetID( "enemy_laser" ) - Laser.Color:SetPriority( 5 ) - Laser.Color:SetOptionsTable( "EnemyLaser" ) - Laser.Color:SetupMenu( Laser.MenuId ) - - -- Rainbow Laser - MenuCallbackHandler.toggle_enemy_laser_rainbow = function(this, item) - GoonBase.Options.EnemyLaser.Rainbow = item:value() == "on" and true or false - GoonBase.Options:Save() - end - - MenuCallbackHandler.enemy_laser_rainbow_speed = function(this, item) - GoonBase.Options.EnemyLaser.RainbowSpeed = item:value() - GoonBase.Options:Save() - end - - GoonBase.MenuHelper:AddToggle({ - id = "toggle_enemy_laser_rainbow", - title = "Options_EnemyLaserRainbowTitle", - desc = "Options_EnemyLaserRainbowDesc", - callback = "toggle_enemy_laser_rainbow", - value = GoonBase.Options.EnemyLaser.Rainbow, - menu_id = Laser.MenuId, - priority = 2 - }) - - GoonBase.MenuHelper:AddSlider({ - id = "enemy_laser_rainbow_speed", - title = "Options_EnemyLaserRainbowSpeedTitle", - desc = "Options_EnemyLaserRainbowSpeedDesc", - callback = "enemy_laser_rainbow_speed", - value = GoonBase.Options.EnemyLaser.RainbowSpeed, - min = 1, - max = 100, - step = 1, - show_value = true, - menu_id = Laser.MenuId, - priority = 1, - }) - -end) - -Hooks:Add("MenuManagerBuildCustomMenus", "MenuManagerBuildCustomMenus_EnemyLaser", function(menu_manager, mainmenu_nodes) - local menu_id = Laser.MenuId - local data = { - area_bg = "half" - } - mainmenu_nodes[menu_id] = GoonBase.MenuHelper:BuildMenu( menu_id, data ) -end) - --- Hooks -Hooks:Add("WeaponLaserPostSetColorByTheme", "WeaponLaserPostSetColorByTheme_CustomEnemyLaser", function(laser, unit) - - if not Laser:IsEnabled() then - return - end - - if not laser._is_npc or Laser:IsNPCPlayerUnitLaser( laser ) then - return - end - - laser:set_color( Laser:GetColor() ) - -end) - -Hooks:Add("WeaponLaserUpdate", "WeaponLaserUpdate_EnemyRainbow", function(laser, unit, t, dt) - - if not Laser:IsEnabled() then - return - end - - if not laser._is_npc or Laser:IsNPCPlayerUnitLaser( laser ) then - return - end - - if not Laser:IsRainbow() then - laser:set_color( Laser:GetColor() ) - end - - if Laser:IsRainbow() then - Laser:GetColor() - local r, g, b = Laser.Color:ToRGB( math.sin(GoonBase.Options.EnemyLaser.RainbowSpeed * t), GoonBase.Options.EnemyLaser.G, GoonBase.Options.EnemyLaser.B ) - laser:set_color( Color(r, g, b) ) - end - -end) --- END OF FILE diff --git a/GoonBase/mods/colors/weapon_flashlight.lua b/GoonBase/mods/colors/weapon_flashlight.lua deleted file mode 100644 index afed3bc..0000000 --- a/GoonBase/mods/colors/weapon_flashlight.lua +++ /dev/null @@ -1,210 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - --- Mod Definition -local Mod = class( BaseMod ) -Mod.id = "CustomWeaponFlashlight" -Mod.Name = "Custom Weapon Flashlight Colour" -Mod.Desc = "Set a custom colour for flashlights attached to your weapons" -Mod.Requirements = {} -Mod.Incompatibilities = {} -Mod.Priority = 1 - -Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() - GoonBase.Mods:RegisterMod( Mod ) -end) - -if not Mod:IsEnabled() then - return -end - --- Weapon Light -GoonBase.WeaponLight = GoonBase.WeaponLight or {} -local Light = GoonBase.WeaponLight -Light.MenuId = "goonbase_weapon_light_menu" -Light.Color = nil - --- Localization -local Localization = GoonBase.Localization -Localization.Options_WeaponLightName = "Weapon Flashlight Color" -Localization.Options_WeaponLightDesc = "Modify the color of weapon flashlights" -Localization.Options_WeaponLightEnableTitle = "Enable Custom Weapon Flashlight Color" -Localization.Options_WeaponLightEnableDesc = "Use the custom set color for Weapon Flashlights" -Localization.Options_WeaponLightRainbowTitle = "Enable Rainbow Light" -Localization.Options_WeaponLightRainbowDesc = "Enable rainbow instead of the set Hue" -Localization.Options_WeaponLightRainbowSpeedTitle = "Rainbow Speed" -Localization.Options_WeaponLightRainbowSpeedDesc = "Set the speed of the rainbow effect" - --- Options -if GoonBase.Options.WeaponLight == nil then - GoonBase.Options.WeaponLight = {} - GoonBase.Options.WeaponLight.Enabled = false - GoonBase.Options.WeaponLight.R = 1 - GoonBase.Options.WeaponLight.G = 0.1 - GoonBase.Options.WeaponLight.B = 0.1 - GoonBase.Options.WeaponLight.HSV = false - GoonBase.Options.WeaponLight.Rainbow = false - GoonBase.Options.WeaponLight.RainbowSpeed = 10 -end - --- Functions -function Light:IsEnabled() - return GoonBase.Options.WeaponLight.Enabled -end - -function Light:IsRainbow() - return GoonBase.Options.WeaponLight.Rainbow -end - -function Light:GetColor(alpha) - if Light.Color == nil then - Light.Color = ColorHSVRGB:new() - Light.Color:SetOptionsTable( "WeaponLight" ) - end - return Light.Color:GetColor( alpha ) or Color( alpha or 1, 1, 0, 0 ) -end - --- Menu -Hooks:Add("MenuManagerSetupCustomMenus", "MenuManagerSetupCustomMenus_WeaponLight", function(menu_manager, menu_nodes) - GoonBase.MenuHelper:NewMenu( Light.MenuId ) -end) - -Hooks:Add("MenuManagerSetupGoonBaseMenu", "MenuManagerSetupGoonBaseMenu_WeaponLight", function(menu_manager, menu_nodes) - - -- Submenu Button - GoonBase.MenuHelper:AddButton({ - id = "weapon_light_button", - title = "Options_WeaponLightName", - desc = "Options_WeaponLightDesc", - next_node = Light.MenuId, - menu_id = "goonbase_options_menu", - }) - - -- Enabled Toggle - MenuCallbackHandler.toggle_custom_weapon_light_color = function(this, item) - GoonBase.Options.WeaponLight.Enabled = item:value() == "on" and true or false - GoonBase.Options:Save() - end - - GoonBase.MenuHelper:AddToggle({ - id = "toggle_custom_weapon_light_color", - title = "Options_WeaponLightEnableTitle", - desc = "Options_WeaponLightEnableDesc", - callback = "toggle_custom_weapon_light_color", - value = GoonBase.Options.WeaponLight.Enabled, - menu_id = Light.MenuId, - priority = 11 - }) - - -- RGB/HSV Colour - Light.Color = ColorHSVRGB:new() - Light.Color:SetID( "weapon_light" ) - Light.Color:SetPriority( 5 ) - Light.Color:SetOptionsTable( "WeaponLight" ) - Light.Color:SetupMenu( Light.MenuId ) - - -- Rainbow Laser - MenuCallbackHandler.toggle_weapon_light_rainbow = function(this, item) - GoonBase.Options.WeaponLight.Rainbow = item:value() == "on" and true or false - GoonBase.Options:Save() - end - - MenuCallbackHandler.weapon_light_rainbow_speed = function(this, item) - GoonBase.Options.WeaponLight.RainbowSpeed = item:value() - GoonBase.Options:Save() - end - - GoonBase.MenuHelper:AddToggle({ - id = "toggle_weapon_light_rainbow", - title = "Options_WeaponLightRainbowTitle", - desc = "Options_WeaponLightRainbowDesc", - callback = "toggle_weapon_light_rainbow", - value = GoonBase.Options.WeaponLight.Rainbow, - menu_id = Light.MenuId, - priority = 2 - }) - - GoonBase.MenuHelper:AddSlider({ - id = "weapon_light_rainbow_speed", - title = "Options_WeaponLightRainbowSpeedTitle", - desc = "Options_WeaponLightRainbowSpeedDesc", - callback = "weapon_light_rainbow_speed", - value = GoonBase.Options.WeaponLight.RainbowSpeed, - min = 1, - max = 100, - step = 1, - show_value = true, - menu_id = Light.MenuId, - priority = 1, - }) - -end) - -Hooks:Add("MenuManagerBuildCustomMenus", "MenuManagerBuildCustomMenus_WeaponLight", function(menu_manager, mainmenu_nodes) - local menu_id = Light.MenuId - local data = { - area_bg = "half" - } - mainmenu_nodes[menu_id] = GoonBase.MenuHelper:BuildMenu( menu_id, data ) -end) - --- Hooks -Hooks:Add("WeaponFlashLightInit", "WeaponFlashLightInit_CustomLight", function(flashlight, unit) - - if not Light:IsEnabled() then - Hooks:Remove("WeaponFlashLightInit_CustomLight") - return - end - - flashlight._light:set_color( Light:GetColor() ) - -end) - -Hooks:Add("WeaponFlashLightCheckState", "WeaponFlashLightCheckState_CustomLight", function(flashlight) - - if flashlight._on then - - flashlight._unit:set_extension_update_enabled( Idstring("base"), flashlight._on ) - - Hooks:RegisterHook("WeaponFlashLightUpdate") - flashlight._old_update = flashlight.update - flashlight.update = function(self, unit, t, dt) - Hooks:Call( "WeaponFlashLightUpdate", self, unit, t, dt ) - if flashlight.overkill_update ~= nil then - flashlight.overkill_update(self, unit, t, dt) - end - end - - end - -end) - -Hooks:Add("WeaponFlashLightUpdate", "WeaponFlashLightUpdate_Rainbow", function(flashlight, unit, t, dt) - - local psuccess, perror = pcall(function() - - if not Light:IsEnabled() then - return - end - - if not Light:IsRainbow() then - flashlight._light:set_color( Light:GetColor() ) - end - - if Light:IsRainbow() then - - Light:GetColor() - local r, g, b = Light.Color:ToRGB( math.sin(GoonBase.Options.WeaponLight.RainbowSpeed * t), GoonBase.Options.WeaponLight.G, GoonBase.Options.WeaponLight.B ) - flashlight._light:set_color( Color(r * 2, g * 2, b * 2) ) - - end - - end) - if not psuccess then - Print("[Error] " .. perror) - end - -end) --- END OF FILE diff --git a/GoonBase/mods/colors/weapon_laser.lua b/GoonBase/mods/colors/weapon_laser.lua deleted file mode 100644 index 09109be..0000000 --- a/GoonBase/mods/colors/weapon_laser.lua +++ /dev/null @@ -1,397 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - --- Mod Definition -local Mod = class( BaseMod ) -Mod.id = "CustomWeaponLaser" -Mod.Name = "Custom Weapon Laser Colour" -Mod.Desc = "Set a custom colour for lasers attached to your weapons" -Mod.Requirements = {} -Mod.Incompatibilities = {} -Mod.Priority = 1 - -Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() - GoonBase.Mods:RegisterMod( Mod ) -end) - -if not Mod:IsEnabled() then - return -end - --- Weapon Laser -GoonBase.WeaponLaser = GoonBase.WeaponLaser or {} -local Laser = GoonBase.WeaponLaser -Laser.MenuId = "goonbase_weapon_laser_menu" -Laser.Color = nil -Laser.UniquePlayerColours = { - [1] = Color("29ce31"), -- MrGreen - [2] = Color("00eae8"), -- MrBlue - [3] = Color("f99d1c"), -- MrBrown - [4] = Color("ebe818"), -- MrOrange - [5] = Color("ebe818"), -- MrAI -} -Laser.OtherColours = {} - --- Networking -Laser.Network = Laser.Network or {} -local Network = Laser.Network -Network.SendLaserColour = "CustomLaserColour" - --- Localization -local Localization = GoonBase.Localization -Localization.Options_WeaponLaserName = "Weapon Laser Color" -Localization.Options_WeaponLaserDesc = "Modify the color of weapon lasers" -Localization.Options_WeaponLaserEnableTitle = "Enable Custom Weapon Laser Color" -Localization.Options_WeaponLaserEnableDesc = "Use the custom set color for Weapon Lasers" -Localization.Options_WeaponLaserRainbowTitle = "Enable Rainbow Laser" -Localization.Options_WeaponLaserRainbowDesc = "Enable rainbow instead of the set Hue" -Localization.Options_WeaponLaserRainbowSpeedTitle = "Rainbow Speed" -Localization.Options_WeaponLaserRainbowSpeedDesc = "Set the speed of the rainbow effect" -Localization.Options_TeammateLaserOption = "Teammate Lasers" -Localization.Options_TeammateLaserOptionDesc = "Set how teammate lasers should appear" -Localization.Options_TeammateLaser_Same = "Use My Colour" -Localization.Options_TeammateLaser_Theirs = "Use Their Colour" -Localization.Options_TeammateLaser_Unique = "Unique per Person" - --- Options -if GoonBase.Options.WeaponLaser == nil then - GoonBase.Options.WeaponLaser = {} - GoonBase.Options.WeaponLaser.Enabled = false - GoonBase.Options.WeaponLaser.R = 1 - GoonBase.Options.WeaponLaser.G = 0.1 - GoonBase.Options.WeaponLaser.B = 0.1 - GoonBase.Options.WeaponLaser.HSV = false - GoonBase.Options.WeaponLaser.Rainbow = false - GoonBase.Options.WeaponLaser.RainbowSpeed = 10 - GoonBase.Options.WeaponLaser.TeammateLasers = 1 -end - --- Functions -function Laser:IsEnabled() - return GoonBase.Options.WeaponLaser.Enabled -end - -function Laser:IsRainbow() - return GoonBase.Options.WeaponLaser.Rainbow -end - -function Laser:GetColor( alpha ) - if Laser.Color == nil then - Laser.Color = ColorHSVRGB:new() - Laser.Color:SetOptionsTable( "WeaponLaser" ) - end - return Laser.Color:GetColor( alpha ) or Color( alpha or 1, 1, 0, 0 ) -end - -function Laser:GetCriminalNameFromLaserUnit( laser ) - - if not self._laser_units_lookup then - self._laser_units_lookup = {} - end - - local laser_key = nil - if laser._unit then - laser_key = laser._unit:key() - end - if laser_key and self._laser_units_lookup[laser_key] ~= nil then - return self._laser_units_lookup[laser_key] - end - - local criminals_manager = managers.criminals - if not criminals_manager then - return - end - - for id, data in pairs(criminals_manager._characters) do - if data.unit ~= nil and alive(data.unit) and data.name ~= criminals_manager:local_character_name() then - - if data.unit:inventory() and data.unit:inventory():equipped_unit() then - - local wep_base = data.unit:inventory():equipped_unit():base() - if wep_base then - - if wep_base._factory_id ~= nil and wep_base._blueprint ~= nil then - - local gadgets = managers.weapon_factory:get_parts_from_weapon_by_type_or_perk("gadget", wep_base._factory_id, wep_base._blueprint) - if gadgets then - local gadget - for _, i in ipairs(gadgets) do - - gadget = wep_base._parts[i] - gadget = gadget.unit:base() - - if gadget == laser then - if laser_key then - self._laser_units_lookup[laser_key] = data.name - end - return data.name - end - - end - end - - end - - end - - end - - end - end - - if laser_key then - self._laser_units_lookup[laser_key] = false - end - return nil - -end - -function Laser:UsingSameColour() - return GoonBase.Options.WeaponLaser.TeammateLasers == 1 -end - -function Laser:UsingUniqueColour() - return GoonBase.Options.WeaponLaser.TeammateLasers == 2 -end - -function Laser:UsingPlayerColour() - return GoonBase.Options.WeaponLaser.TeammateLasers == 3 -end - --- Menu -Hooks:Add("MenuManagerSetupCustomMenus", "MenuManagerSetupCustomMenus_" .. Mod:ID(), function(menu_manager, menu_nodes) - GoonBase.MenuHelper:NewMenu( Laser.MenuId ) -end) - -Hooks:Add("MenuManagerSetupGoonBaseMenu", "MenuManagerSetupGoonBaseMenu_" .. Mod:ID(), function(menu_manager, menu_nodes) - - -- Submenu Button - GoonBase.MenuHelper:AddButton({ - id = "weapon_laser_button", - title = "Options_WeaponLaserName", - desc = "Options_WeaponLaserDesc", - next_node = Laser.MenuId, - menu_id = "goonbase_options_menu", - }) - - -- Enabled Toggle - MenuCallbackHandler.toggle_custom_weapon_laser_color = function(this, item) - GoonBase.Options.WeaponLaser.Enabled = item:value() == "on" and true or false - GoonBase.Options:Save() - end - - GoonBase.MenuHelper:AddToggle({ - id = "toggle_custom_weapon_laser_color", - title = "Options_WeaponLaserEnableTitle", - desc = "Options_WeaponLaserEnableDesc", - callback = "toggle_custom_weapon_laser_color", - value = GoonBase.Options.WeaponLaser.Enabled, - menu_id = Laser.MenuId, - priority = 50 - }) - - -- RGB/HSV Colour - Laser.Color = ColorHSVRGB:new() - Laser.Color:SetID( "weapon_laser" ) - Laser.Color:SetPriority( 30 ) - Laser.Color:SetOptionsTable( "WeaponLaser" ) - Laser.Color:SetupMenu( Laser.MenuId ) - - -- Rainbow Laser - MenuCallbackHandler.toggle_weapon_laser_rainbow = function(this, item) - GoonBase.Options.WeaponLaser.Rainbow = item:value() == "on" and true or false - GoonBase.Options:Save() - end - - MenuCallbackHandler.weapon_laser_rainbow_speed = function(this, item) - GoonBase.Options.WeaponLaser.RainbowSpeed = item:value() - GoonBase.Options:Save() - end - - MenuCallbackHandler.weapon_laser_set_teammate = function(this, item) - GoonBase.Options.WeaponLaser.TeammateLasers = tonumber(item:value()) - GoonBase.Options:Save() - end - - GoonBase.MenuHelper:AddToggle({ - id = "toggle_weapon_laser_rainbow", - title = "Options_WeaponLaserRainbowTitle", - desc = "Options_WeaponLaserRainbowDesc", - callback = "toggle_weapon_laser_rainbow", - value = GoonBase.Options.WeaponLaser.Rainbow, - menu_id = Laser.MenuId, - priority = 25 - }) - - GoonBase.MenuHelper:AddSlider({ - id = "weapon_laser_rainbow_speed", - title = "Options_WeaponLaserRainbowSpeedTitle", - desc = "Options_WeaponLaserRainbowSpeedDesc", - callback = "weapon_laser_rainbow_speed", - value = GoonBase.Options.WeaponLaser.RainbowSpeed, - min = 1, - max = 100, - step = 1, - show_value = true, - menu_id = Laser.MenuId, - priority = 24, - }) - - GoonBase.MenuHelper:AddDivider({ - id = "weapon_laser_divider", - menu_id = Laser.MenuId, - size = 16, - priority = 21, - }) - - GoonBase.MenuHelper:AddMultipleChoice({ - id = "weapon_laser_teammate_choice", - title = "Options_TeammateLaserOption", - desc = "Options_TeammateLaserOptionDesc", - callback = "weapon_laser_set_teammate", - menu_id = Laser.MenuId, - priority = 20, - items = { - [1] = "Options_TeammateLaser_Same", - [2] = "Options_TeammateLaser_Unique", - [3] = "Options_TeammateLaser_Theirs", - }, - value = GoonBase.Options.WeaponLaser.TeammateLasers, - }) - -end) - -Hooks:Add("MenuManagerBuildCustomMenus", "MenuManagerBuildCustomMenus_" .. Mod:ID(), function(menu_manager, mainmenu_nodes) - local menu_id = Laser.MenuId - local data = { - area_bg = "half" - } - mainmenu_nodes[menu_id] = GoonBase.MenuHelper:BuildMenu( menu_id, data ) -end) - --- Hooks -Hooks:Add("WeaponLaserInit", "WeaponLaserInit_" .. Mod:ID(), function(laser, unit) - Laser:UpdateLaser(laser, unit, 0, 0) -end) - -Hooks:Add("WeaponLaserUpdate", "WeaponLaserUpdate_Rainbow_" .. Mod:ID(), function(laser, unit, t, dt) - Laser:UpdateLaser(laser, unit, t, dt) -end) - -function Laser:UpdateLaser( laser, unit, t, dt ) - - local psuccess, perror = pcall(function() - - if not Laser:IsEnabled() then - return - end - - if laser._is_npc then - - local criminal_name = Laser:GetCriminalNameFromLaserUnit( laser ) - if not criminal_name then - return - end - - if Laser:UsingSameColour() then - Laser:SetColourOfLaser( laser, unit, t, dt ) - end - - if Laser:UsingUniqueColour() then - local id = managers.criminals:character_color_id_by_name( criminal_name ) - if id == 1 then id = id + 1 end - local col = Laser.UniquePlayerColours[ id or 5 ] - Laser:SetColourOfLaser( laser, unit, t, dt, col ) - end - - if Laser:UsingPlayerColour() then - local col = Laser.OtherColours[criminal_name] - Laser:SetColourOfLaser( laser, unit, t, dt, col ) - end - - return - - end - - Laser:SetColourOfLaser( laser, unit, t, dt ) - - end) - if not psuccess then - Print("[Error] " .. perror) - end - -end - -function Laser:SetColourOfLaser( laser, unit, t, dt, colour_override ) - - if colour_override ~= nil and colour_override ~= "rainbow" then - local psuccess, perror = pcall(function() - laser:set_color( colour_override:with_alpha(0.4) ) - end) - if not psuccess then - Print("[Error] " .. perror) - end - return - end - - if not Laser:IsRainbow() then - laser:set_color( Laser:GetColor(0.4) ) - end - - if Laser:IsRainbow() or colour_override == "rainbow" then - Laser:GetColor() - local r, g, b = Laser.Color:ToRGB( math.sin(GoonBase.Options.WeaponLaser.RainbowSpeed * t), GoonBase.Options.WeaponLaser.G, GoonBase.Options.WeaponLaser.B ) - laser:set_color( Color(r, g, b):with_alpha(0.4) ) - end - -end - --- Networked Colour -Hooks:Add("WeaponLaserSetOn", "WeaponLaserSetOn_" .. Mod:ID(), function(laser) - - if laser._is_npc then - return - end - - local criminals_manager = managers.criminals - if not criminals_manager then - return - end - - local local_name = criminals_manager:local_character_name() - local laser_name = Laser:GetCriminalNameFromLaserUnit( laser ) - if laser_name == nil or local_name == laser_name then - local col_str = GoonBase.Network:PrepareNetworkedColourString( Laser:GetColor() ) - if Laser:IsRainbow() then - col_str = "rainbow" - end - GoonBase.Network:SendToPeers( Network.SendLaserColour, col_str ) - end - -end) - -Hooks:Add("NetworkReceivedData", "NetworkReceivedData_" .. Mod:ID(), function(sender, message, data) - - if message == Network.SendLaserColour then - - local criminals_manager = managers.criminals - if not criminals_manager then - return - end - - local char = criminals_manager:character_name_by_peer_id(sender) - local col = data - if data ~= "rainbow" then - col = GoonBase.Network:NetworkedColourStringToColour(data) - end - - if char then - Laser.OtherColours[char] = col - end - - end - -end) --- END OF FILE diff --git a/GoonBase/mods/colors/world_laser_colors.lua b/GoonBase/mods/colors/world_laser_colors.lua deleted file mode 100644 index 3c4e6ec..0000000 --- a/GoonBase/mods/colors/world_laser_colors.lua +++ /dev/null @@ -1,181 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - --- Mod Definition -local Mod = class( BaseMod ) -Mod.id = "CustomWorldLaserColour" -Mod.Name = "Custom World Laser Colour" -Mod.Desc = "Set a custom colour for lasers that appear in the game world" -Mod.Requirements = {} -Mod.Incompatibilities = {} -Mod.Priority = 1 - -Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() - GoonBase.Mods:RegisterMod( Mod ) -end) - -if not Mod:IsEnabled() then - return -end - --- Lasers -GoonBase.WorldLasers = GoonBase.WorldLasers or {} -local Lasers = GoonBase.WorldLasers -Lasers.MenuId = "goonbase_world_laser_menu" -Lasers.Color = nil - --- Localization -local Localization = GoonBase.Localization -Localization.Options_WorldLaserName = "World Laser Color" -Localization.Options_WorldLaserDesc = "Modify the color of lasers that appear in the world" -Localization.Options_WorldLaserEnableTitle = "Enable Custom World Laser Color" -Localization.Options_WorldLaserEnableDesc = "Use the custom set color for World Lasers" -Localization.Options_WorldLaserRainbowTitle = "Enable Rainbow Laser" -Localization.Options_WorldLaserRainbowDesc = "Enable rainbow instead of the set Hue" -Localization.Options_WorldLaserRainbowSpeedTitle = "Rainbow Speed" -Localization.Options_WorldLaserRainbowSpeedDesc = "Set the speed of the rainbow effect" - --- Options -if GoonBase.Options.WorldLasers == nil then - GoonBase.Options.WorldLasers = {} - GoonBase.Options.WorldLasers.Enabled = false - GoonBase.Options.WorldLasers.R = 1 - GoonBase.Options.WorldLasers.G = 0.1 - GoonBase.Options.WorldLasers.B = 0.1 - GoonBase.Options.WorldLasers.HSV = false - GoonBase.Options.WorldLasers.Rainbow = false - GoonBase.Options.WorldLasers.RainbowSpeed = 10 -end - --- Functions -function Lasers:IsEnabled() - return GoonBase.Options.WorldLasers.Enabled -end - -function Lasers:IsRainbow() - return GoonBase.Options.WorldLasers.Rainbow -end - -function Lasers:GetColor(alpha) - if Lasers.Color == nil then - Lasers.Color = ColorHSVRGB:new() - Lasers.Color:SetOptionsTable( "WorldLasers" ) - end - return Lasers.Color:GetColor( alpha ) or Color( alpha or 1, 1, 0, 0 ) -end - --- Menu -Hooks:Add("MenuManagerSetupCustomMenus", "MenuManagerSetupCustomMenus_WorldLaser", function(menu_manager, menu_nodes) - GoonBase.MenuHelper:NewMenu( Lasers.MenuId ) -end) - -Hooks:Add("MenuManagerSetupGoonBaseMenu", "MenuManagerSetupGoonBaseMenu_WorldLaser", function(menu_manager, menu_nodes) - - -- Submenu Button - GoonBase.MenuHelper:AddButton({ - id = "world_laser_button", - title = "Options_WorldLaserName", - desc = "Options_WorldLaserDesc", - next_node = Lasers.MenuId, - menu_id = "goonbase_options_menu", - }) - - -- Enabled Toggle - MenuCallbackHandler.toggle_custom_world_laser_color = function(this, item) - GoonBase.Options.WorldLasers.Enabled = item:value() == "on" and true or false - GoonBase.Options:Save() - end - - GoonBase.MenuHelper:AddToggle({ - id = "toggle_custom_world_laser_color", - title = "Options_WorldLaserEnableTitle", - desc = "Options_WorldLaserEnableDesc", - callback = "toggle_custom_world_laser_color", - value = GoonBase.Options.WorldLasers.Enabled, - menu_id = Lasers.MenuId, - priority = 11 - }) - - -- RGB/HSV Colour - Lasers.Color = ColorHSVRGB:new() - Lasers.Color:SetID( "world_laser" ) - Lasers.Color:SetPriority( 5 ) - Lasers.Color:SetOptionsTable( "WorldLasers" ) - Lasers.Color:SetupMenu( Lasers.MenuId ) - - -- Rainbow Laser - MenuCallbackHandler.toggle_world_laser_rainbow = function(this, item) - GoonBase.Options.WorldLasers.Rainbow = item:value() == "on" and true or false - GoonBase.Options:Save() - end - - MenuCallbackHandler.world_laser_rainbow_speed = function(this, item) - GoonBase.Options.WorldLasers.RainbowSpeed = item:value() - GoonBase.Options:Save() - end - - GoonBase.MenuHelper:AddToggle({ - id = "toggle_world_laser_rainbow", - title = "Options_WorldLaserRainbowTitle", - desc = "Options_WorldLaserRainbowDesc", - callback = "toggle_world_laser_rainbow", - value = GoonBase.Options.WorldLasers.Rainbow, - menu_id = Lasers.MenuId, - priority = 2 - }) - - GoonBase.MenuHelper:AddSlider({ - id = "world_laser_rainbow_speed", - title = "Options_WorldLaserRainbowSpeedTitle", - desc = "Options_WorldLaserRainbowSpeedDesc", - callback = "world_laser_rainbow_speed", - value = GoonBase.Options.WorldLasers.RainbowSpeed, - min = 1, - max = 100, - step = 1, - show_value = true, - menu_id = Lasers.MenuId, - priority = 1, - }) - -end) - -Hooks:Add("MenuManagerBuildCustomMenus", "MenuManagerBuildCustomMenus_WorldLaser", function(menu_manager, mainmenu_nodes) - local menu_id = Lasers.MenuId - local data = { - area_bg = "half" - } - mainmenu_nodes[menu_id] = GoonBase.MenuHelper:BuildMenu( menu_id, data ) -end) - --- Hooks -Hooks:Add("ElementLaserTriggerPostInit", "ElementLaserTriggerPostInit_WorldLaser", function(laser) - - if not Lasers:IsEnabled() then - return - end - - laser._brush:set_color( Lasers:GetColor(0.15) ) - -end) - -Hooks:Add("ElementLaserTriggerUpdateDraw", "ElementLaserTriggerUpdateDraw_WorldLaser", function(laser, t, dt) - - if not Lasers:IsEnabled() then - return - end - - if not Lasers:IsRainbow() then - laser._brush:set_color( Lasers:GetColor() ) - end - - if Lasers:IsRainbow() then - Lasers:GetColor() - local r, g, b = Lasers.Color:ToRGB( math.sin(GoonBase.Options.WorldLasers.RainbowSpeed * t), GoonBase.Options.WorldLasers.G, GoonBase.Options.WorldLasers.B ) - laser._brush:set_color( Color(0.2, r, g, b) ) - end - -end) --- END OF FILE diff --git a/GoonBase/mods/colour_grading.lua b/GoonBase/mods/colour_grading.lua deleted file mode 100644 index 66ef4a6..0000000 --- a/GoonBase/mods/colour_grading.lua +++ /dev/null @@ -1,198 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - --- Mod Definition -local Mod = class( BaseMod ) -Mod.id = "GameColourGrading" -Mod.Name = "Colour Grading" -Mod.Desc = "Allows you to use the colour grading options from Payday: The Heist" -Mod.Requirements = {} -Mod.Incompatibilities = {} - -Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() - GoonBase.Mods:RegisterMod( Mod ) -end) - -if not Mod:IsEnabled() then - return -end - --- Colour Grading -GoonBase.ColourGrading = GoonBase.ColourGrading or {} -local ColourGrading = GoonBase.ColourGrading -ColourGrading.MenuId = "goonbase_colour_grading" -ColourGrading.GradingOptions = { - "color_off", - "color_payday", - "color_heat", - "color_nice", - "color_sin", - "color_bhd", - "color_xgen", - "color_xxxgen", - "color_matrix" -} - --- Localization -local Localization = GoonBase.Localization -Localization.Options_ColourGradingName = "Colour Grading" -Localization.Options_ColourGradingDesc = "Allows you to change the colour grading post-FX" -Localization.Option_ColourGradingName = "Colour Grading" -Localization.Option_ColourGradingDesc = "Specify a colour grading scheme to use instead of the default" -Localization.Option_Menu_ColourGradingName = "Menu Colour Grading" -Localization.Option_Menu_ColourGradingDesc = "Specify a colour grading scheme to use on the main menu instead of the default" -Localization.GradingOption_Off = "Default" -Localization.GradingOption_PaydayPlus = "Payday+" -Localization.GradingOption_Heat = "Dinero" -Localization.GradingOption_Nice = "In Traffic" -Localization.GradingOption_Sin = "GenSec" -Localization.GradingOption_BHD = "BHD" -Localization.GradingOption_XGen = "XGen Brown" -Localization.GradingOption_XXXGen = "Future of Gaming" -Localization.GradingOption_Matrix = "The Matrices" - --- Options -if GoonBase.Options.ColourGrading == nil then - GoonBase.Options.ColourGrading = {} - GoonBase.Options.ColourGrading.Enabled = false - GoonBase.Options.ColourGrading.GradingOption = 1 - GoonBase.Options.ColourGrading.GradingOptionMenu = 1 -end - --- Functions -function ColourGrading:ColourGradingEnabled( is_menu ) - if not GoonBase.Options.ColourGrading then - return false - end - if not GoonBase.Options.ColourGrading.GradingOption then - return false - end - local i = is_menu and GoonBase.Options.ColourGrading.GradingOptionMenu or GoonBase.Options.ColourGrading.GradingOption - return i > 1 -end - -function ColourGrading:GetGradingIndex( is_menu ) - if not GoonBase.Options.ColourGrading then - return 1 - end - if not GoonBase.Options.ColourGrading.GradingOption then - return 1 - end - return is_menu and GoonBase.Options.ColourGrading.GradingOptionMenu or GoonBase.Options.ColourGrading.GradingOption -end - -function ColourGrading:GetGradingOption( is_menu ) - if is_menu == nil then - is_menu = not ColourGrading:IsInGame() - end - if ColourGrading:ColourGradingEnabled( is_menu ) then - return ColourGrading.GradingOptions[ ColourGrading:GetGradingIndex(is_menu) ] - else - return ColourGrading:GetDefaultGradingOption( is_menu ) - end -end - -function ColourGrading:GetDefaultGradingOption( is_menu ) - return is_menu and "color_matrix" or "color_off" -end - -function ColourGrading:IsInGame() - if not game_state_machine then - return false - end - return string.find( game_state_machine:current_state_name(), "ingame" ) and true or false -end - -function ColourGrading:UpdateColourGrading() - if managers and managers.environment_controller then - local grading = ColourGrading:GetGradingOption() - managers.environment_controller:set_default_color_grading( grading ) - managers.environment_controller:refresh_render_settings() - end -end - --- Menu -Hooks:Add("MenuManagerSetupCustomMenus", "MenuManagerSetupCustomMenus_" .. Mod:ID(), function(menu_manager, menu_nodes) - GoonBase.MenuHelper:NewMenu( ColourGrading.MenuId ) -end) - -Hooks:Add("MenuManagerSetupGoonBaseMenu", "MenuManagerSetupGoonBaseMenu_" .. Mod:ID(), function(menu_manager, menu_nodes) - - -- Submenu Button - GoonBase.MenuHelper:AddButton({ - id = "colour_grading_override", - title = "Options_ColourGradingName", - desc = "Options_ColourGradingDesc", - next_node = ColourGrading.MenuId, - menu_id = "goonbase_options_menu", - }) - - -- Menu - MenuCallbackHandler.colour_grading_set_override = function(this, item) - GoonBase.Options.ColourGrading.GradingOption = tonumber(item:value()) - GoonBase.Options:Save() - ColourGrading:UpdateColourGrading() - end - - MenuCallbackHandler.colour_grading_set_override_menu = function(this, item) - GoonBase.Options.ColourGrading.GradingOptionMenu = tonumber(item:value()) - GoonBase.Options:Save() - ColourGrading:UpdateColourGrading() - end - - GoonBase.MenuHelper:AddMultipleChoice({ - id = "colour_grading_override_selector", - title = "Option_ColourGradingName", - desc = "Option_ColourGradingDesc", - callback = "colour_grading_set_override", - menu_id = ColourGrading.MenuId, - items = { - [1] = "GradingOption_Off", - [2] = "GradingOption_PaydayPlus", - [3] = "GradingOption_Heat", - [4] = "GradingOption_Nice", - [5] = "GradingOption_Sin", - [6] = "GradingOption_BHD", - [7] = "GradingOption_XGen", - [8] = "GradingOption_XXXGen", - [9] = "GradingOption_Matrix", - }, - value = GoonBase.Options.ColourGrading.GradingOption, - }) - - GoonBase.MenuHelper:AddMultipleChoice({ - id = "colour_grading_override_selector_menu", - title = "Option_Menu_ColourGradingName", - desc = "Option_Menu_ColourGradingDesc", - callback = "colour_grading_set_override_menu", - menu_id = ColourGrading.MenuId, - items = { - [1] = "GradingOption_Off", - [2] = "GradingOption_PaydayPlus", - [3] = "GradingOption_Heat", - [4] = "GradingOption_Nice", - [5] = "GradingOption_Sin", - [6] = "GradingOption_BHD", - [7] = "GradingOption_XGen", - [8] = "GradingOption_XXXGen", - [9] = "GradingOption_Matrix", - }, - value = GoonBase.Options.ColourGrading.GradingOptionMenu, - }) - -end) - -Hooks:Add("MenuManagerBuildCustomMenus", "MenuManagerBuildCustomMenus_" .. Mod:ID(), function(menu_manager, mainmenu_nodes) - local menu_id = ColourGrading.MenuId - local data = { - area_bg = "half" - } - mainmenu_nodes[menu_id] = GoonBase.MenuHelper:BuildMenu( menu_id, data ) -end) - -Hooks:Add("MenuManagerOnOpenMenu", "MenuManagerOnOpenMenu_" .. Mod:ID(), function( menu_manager, menu, position ) - ColourGrading:UpdateColourGrading() -end) --- END OF FILE diff --git a/GoonBase/mods/custom_grenades.lua b/GoonBase/mods/custom_grenades.lua deleted file mode 100644 index bd28e2d..0000000 --- a/GoonBase/mods/custom_grenades.lua +++ /dev/null @@ -1,33 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -Hooks:Add("BlackMarketManagerPostSetup", "BlackMarketManagerPostSetup_CustomGrenades", function(blackmarket_manager) - - -- blackmarket_manager._defaults.grenade = "flashbang" - -end) - -Hooks:Add("BlackMarketTweakDataPostInitGrenades", "BlackMarketTweakDataPostInitGrenades_CustomGrenades", function(tweak_data) - - tweak_data.grenades.flashbang = {} - tweak_data.grenades.flashbang.name_id = "bm_grenade_flashbang" - tweak_data.grenades.flashbang.unit = "units/payday2/weapons/wpn_frag_grenade/wpn_frag_grenade" - tweak_data.grenades.flashbang.unit_dummy = "units/payday2/weapons/wpn_frag_grenade/wpn_frag_grenade_husk" - tweak_data.grenades.flashbang.sprint_unit = "units/payday2/weapons/wpn_frag_grenade/wpn_frag_grenade_sprint" - tweak_data.grenades.flashbang.icon = "frag_grenade" - tweak_data.grenades.flashbang.dlc = "gage_pack" - -end) - -Hooks:Add("GrenadeBaseClassInit", "GrenadeBaseClassInit_CustomGrenades", function(grenade_base) - - table.insert( grenade_base.types, "flashbang" ) - table.insert( grenade_base.types, "smoke" ) - table.insert( grenade_base.types, "incendiary" ) - - dofile( "GoonBase/mods/weapons/flashbang_grenade.lua" ) - -end) --- END OF FILE diff --git a/GoonBase/mods/custom_waypoints.lua b/GoonBase/mods/custom_waypoints.lua deleted file mode 100644 index cdebe4e..0000000 --- a/GoonBase/mods/custom_waypoints.lua +++ /dev/null @@ -1,273 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - --- Mod Definition -local Mod = class( BaseMod ) -Mod.id = "CustomWaypoints" -Mod.Name = "Custom Waypoints" -Mod.Desc = "Allows players to set waypoints for themselves and friends" -Mod.Requirements = {} -Mod.Incompatibilities = {} - -Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() - GoonBase.Mods:RegisterMod( Mod ) -end) - -if not Mod:IsEnabled() then - return -end - --- Custom Waypoints -_G.GoonBase.CustomWaypoints = _G.GoonBase.CustomWaypoints or {} -local CustomWaypoints = _G.GoonBase.CustomWaypoints -CustomWaypoints.MenuID = "goonbase_custom_waypoints_menu" -CustomWaypoints.PlaceWaypointName = "GoonBasePlaceWaypoint" -CustomWaypoints.RemoveWaypointName = "GoonBaseRemoveWaypoint" -CustomWaypoints.CustomKeys = { - PLACE = GoonBase.Options.CustomWaypoints ~= nil and GoonBase.Options.CustomWaypoints.PlaceWaypoint or "k", - REMOVE = GoonBase.Options.CustomWaypoints ~= nil and GoonBase.Options.CustomWaypoints.RemoveWaypoint or "l" -} - --- Network -CustomWaypoints.Network = {} -CustomWaypoints.Network.PlaceWaypoint = "CustomWaypointPlace" -CustomWaypoints.Network.RemoveWaypoint = "CustomWaypointRemove" - --- Options -if GoonBase.Options.CustomWaypoints == nil then - GoonBase.Options.CustomWaypoints = {} - GoonBase.Options.CustomWaypoints.PlaceWaypoint = "k" - GoonBase.Options.CustomWaypoints.RemoveWaypoint = "l" - GoonBase.Options.CustomWaypoints.ShowDistance = true -end - --- Localization -local Localization = GoonBase.Localization -Localization.OptionsMenu_CustomWaypointMenuTitle = "Custom Waypoints" -Localization.OptionsMenu_CustomWaypointMenuMessage = "Change settings for your customizable waypoints" -Localization.OptionsMenu_CustomWaypointKeybindPlace = "Place Waypoint" -Localization.OptionsMenu_CustomWaypointKeybindRemove = "Remove Waypoint" -Localization.OptionsMenu_CustomWaypointShowDistanceTitle = "Show Distance on Waypoints" -Localization.OptionsMenu_CustomWaypointShowDistanceMessage = "Show how far away you are from custom waypoints" - --- Updates -Hooks:Add("GameSetupUpdate", "GameSetupUpdate_" .. Mod:ID(), function(t, dt) - CustomWaypoints:UpdateBindings() -end) - -function CustomWaypoints:UpdateBindings() - - local self = CustomWaypoints - if self._input == nil then - self._input = Input:keyboard() - end - if managers.hud:chat_focus() then - return - end - - local place_key = CustomWaypoints.CustomKeys.PLACE - if not string.is_nil_or_empty(place_key) and self._input:pressed(Idstring(place_key)) then - CustomWaypoints:SetWaypoint() - end - - local remove_key = CustomWaypoints.CustomKeys.REMOVE - if not string.is_nil_or_empty(remove_key) and self._input:pressed(Idstring(remove_key)) then - CustomWaypoints:RemoveWaypoint() - end - -end - --- Custom Key Set -Hooks:Add("CustomizeControllerOnKeySet", "CustomizeControllerOnKeySet_" .. Mod:ID(), function(item) - - if item._name == CustomWaypoints.PlaceWaypointName then - CustomWaypoints.CustomKeys.PLACE = item._input_name_list[1] - CustomWaypoints:SaveBindings() - end - - if item._name == CustomWaypoints.RemoveWaypointName then - CustomWaypoints.CustomKeys.REMOVE = item._input_name_list[1] - CustomWaypoints:SaveBindings() - end - -end) - -function CustomWaypoints:SaveBindings() - GoonBase.Options.CustomWaypoints.PlaceWaypoint = CustomWaypoints.CustomKeys.PLACE - GoonBase.Options.CustomWaypoints.RemoveWaypoint = CustomWaypoints.CustomKeys.REMOVE - GoonBase.Options:Save() -end - --- Menu -Hooks:Add("MenuManagerSetupCustomMenus", "MenuManagerSetupCustomMenus_" .. Mod:ID(), function(menu_manager, menu_nodes) - GoonBase.MenuHelper:NewMenu( CustomWaypoints.MenuID ) -end) - -Hooks:Add("MenuManagerSetupGoonBaseMenu", "MenuManagerSetupGoonBaseMenu_" .. Mod:ID(), function( menu_manager ) - - -- Menu button - GoonBase.MenuHelper:AddButton({ - id = "custom_waypoint_menu_button", - title = "OptionsMenu_CustomWaypointMenuTitle", - desc = "OptionsMenu_CustomWaypointMenuMessage", - next_node = CustomWaypoints.MenuID, - menu_id = "goonbase_options_menu" - }) - - -- Keybinds - GoonBase.MenuHelper:AddKeybinding({ - id = "custom_waypoint_menu_keybind_place", - title = managers.localization:text("OptionsMenu_CustomWaypointKeybindPlace"), - connection_name = CustomWaypoints.PlaceWaypointName, - button = CustomWaypoints.CustomKeys.PLACE, - binding = CustomWaypoints.CustomKeys.PLACE, - menu_id = CustomWaypoints.MenuID, - priority = 10 - }) - - GoonBase.MenuHelper:AddKeybinding({ - id = "custom_waypoint_menu_keybind_remove", - title = managers.localization:text("OptionsMenu_CustomWaypointKeybindRemove"), - connection_name = CustomWaypoints.RemoveWaypointName, - button = CustomWaypoints.CustomKeys.REMOVE, - binding = CustomWaypoints.CustomKeys.REMOVE, - menu_id = CustomWaypoints.MenuID, - priority = 9 - }) - - -- Show Distance - MenuCallbackHandler.toggle_custom_waypoint_distance = function(this, item) - GoonBase.Options.CustomWaypoints.ShowDistance = item:value() == "on" and true or false - GoonBase.Options:Save() - end - - GoonBase.MenuHelper:AddDivider({ - id = "custom_waypoint_menu_divider", - menu_id = CustomWaypoints.MenuID, - size = 16, - priority = 2, - }) - - GoonBase.MenuHelper:AddToggle({ - id = "toggle_custom_waypoint_distance", - title = "OptionsMenu_CustomWaypointShowDistanceTitle", - desc = "OptionsMenu_CustomWaypointShowDistanceMessage", - callback = "toggle_custom_waypoint_distance", - value = GoonBase.Options.CustomWaypoints.ShowDistance, - menu_id = CustomWaypoints.MenuID, - priority = 1, - }) - -end) - -Hooks:Add("MenuManagerBuildCustomMenus", "MenuManagerBuildCustomMenus_" .. Mod:ID(), function(menu_manager, mainmenu_nodes) - mainmenu_nodes[CustomWaypoints.MenuID] = GoonBase.MenuHelper:BuildMenu( CustomWaypoints.MenuID ) -end) - --- Waypoints -function CustomWaypoints:_AddWaypoint( waypoint_name, pos, color_id ) - managers.hud:add_waypoint( - "CustomWaypoint_" .. waypoint_name, - { - icon = "infamy_icon", - distance = GoonBase.Options.CustomWaypoints.ShowDistance, - position = pos, - no_sync = false, - present_timer = 0, - state = "present", - radius = 50, - color = tweak_data.preplanning_peer_colors[color_id or 1], - blend_mode = "add" - } - ) -end - -function CustomWaypoints:_RemoveWaypoint( waypoint_name ) - managers.hud:remove_waypoint("CustomWaypoint_" .. waypoint_name) -end - -function CustomWaypoints:SetWaypoint() - - if managers.player:player_unit() == nil then - return - end - - local psuccess, perror = pcall(function() - - local pos = GetPlayerAimPos( managers.player:player_unit() ) - if not pos then - return - end - - CustomWaypoints:_AddWaypoint( "localplayer", pos, GoonBase.Network:LocalPeerID() ) - - pos = Vector3.ToString( pos ) - GoonBase.Network:SendToPeers( CustomWaypoints.Network.PlaceWaypoint, pos ) - - end) - if not psuccess then - Print("[Error] " .. perror) - end - -end - -function CustomWaypoints:RemoveWaypoint() - - local psuccess, perror = pcall(function() - - GoonBase.Network:SendToPeers( CustomWaypoints.Network.RemoveWaypoint, "" ) - CustomWaypoints:_RemoveWaypoint( "localplayer" ) - - end) - if not psuccess then - Print("[Error] " .. perror) - end - -end - -function CustomWaypoints:NetworkPlace( player, position ) - - local psuccess, perror = pcall(function() - - local ply_name = GoonBase.Network:GetNameFromPeerID(player) - local pos = string.ToVector3(position) - if pos ~= nil then - CustomWaypoints:_AddWaypoint( ply_name, pos, player ) - end - - end) - if not psuccess then - Print("[Error] " .. perror) - end - -end - -function CustomWaypoints:NetworkRemove(player) - - local psuccess, perror = pcall(function() - - local ply_name = GoonBase.Network:GetNameFromPeerID(player) - CustomWaypoints:_RemoveWaypoint( ply_name ) - - end) - if not psuccess then - Print("[Error] " .. perror) - end - -end - --- Networked Data -Hooks:Add("NetworkReceivedData", "NetworkReceivedData_" .. Mod:ID(), function(sender, messageType, data) - - if messageType == CustomWaypoints.Network.PlaceWaypoint then - CustomWaypoints:NetworkPlace(sender, data) - end - - if messageType == CustomWaypoints.Network.RemoveWaypoint then - CustomWaypoints:NetworkRemove(sender) - end - -end) --- END OF FILE diff --git a/GoonBase/mods/extended_inventory.lua b/GoonBase/mods/extended_inventory.lua deleted file mode 100644 index c281b7b..0000000 --- a/GoonBase/mods/extended_inventory.lua +++ /dev/null @@ -1,335 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - --- Mod Definition -local Mod = class( BaseMod ) -Mod.id = "ExtendedInventory" -Mod.Name = "Extended Inventory" -Mod.Desc = "Allows mods to add special items to your inventory" -Mod.Requirements = {} -Mod.Incompatibilities = {} - -Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() - GoonBase.Mods:RegisterMod( Mod ) -end) - -if not Mod:IsEnabled() then - return -end - --- Extended Inventory -_G.GoonBase.ExtendedInventory = _G.GoonBase.ExtendedInventory or {} -local ExtendedInv = _G.GoonBase.ExtendedInventory - --- Localization -local Localization = GoonBase.Localization -Localization.bm_menu_extended_inv = "Special" -Localization.DefaultReserveText = "IN RESERVE" - -ExtendedInv.InitialLoadComplete = false -ExtendedInv.RegisteredItems = {} -ExtendedInv.Items = {} -ExtendedInv.SaveFile = GoonBase.Path .. "inventory.ini" - --- Initialize -Hooks:RegisterHook("ExtendedInventoryInitialized") -Hooks:Add("GoonBasePostLoadedMods", "GoonBasePostLoadedMods_ExtendedInv", function() - Hooks:Call("ExtendedInventoryInitialized") -end) - -function ExtendedInv:_MissingItemError(item) - Print("[Error] Could not find item '" .. item .. "' in Extended Inventory!") -end - -function ExtendedInv:ItemIsRegistered(id) - return ExtendedInv.RegisteredItems[id] == true -end - -function ExtendedInv:RegisterItem(data) - - if not ExtendedInv.InitialLoadComplete then - ExtendedInv:Load() - ExtendedInv.InitialLoadComplete = true - end - - ExtendedInv.RegisteredItems[data.id] = true - ExtendedInv.Items[data.id] = ExtendedInv.Items[data.id] or {} - for k, v in pairs( data ) do - ExtendedInv.Items[data.id][k] = v - end - ExtendedInv.Items[data.id].amount = ExtendedInv.Items[data.id].amount or 0 - -end - -function ExtendedInv:AddItem(item, amount) - if ExtendedInv.Items[item] ~= nil then - ExtendedInv.Items[item].amount = ExtendedInv.Items[item].amount + amount - ExtendedInv:Save() - else - ExtendedInv:_MissingItemError(item) - end -end - -function ExtendedInv:TakeItem(item, amount) - if ExtendedInv.Items[item] ~= nil then - ExtendedInv.Items[item].amount = ExtendedInv.Items[item].amount - amount - ExtendedInv:Save() - else - ExtendedInv:_MissingItemError(item) - end -end - -function ExtendedInv:SetItemAmount(item, amount) - if ExtendedInv.Items[item] ~= nil then - ExtendedInv.Items[item].amount = amount - ExtendedInv:Save() - else - ExtendedInv:_MissingItemError(item) - end -end - - -function ExtendedInv:GetItem(item) - return ExtendedInv.Items[item] or nil -end - -function ExtendedInv:HasItem(item) - if ExtendedInv.Items[item] == nil then - return false - end - return ExtendedInv.Items[item].amount > 0 or nil -end - -function ExtendedInv:GetAllItems() - return ExtendedInv.Items -end - -function ExtendedInv:GetReserveText(item) - return item.reserve_text or Localization.DefaultReserveText -end - -Hooks:Add("BlackMarketGUIPostSetup", "BlackMarketGUIPostSetup_ExtendedInventory", function(gui, is_start_page, component_data) - gui.identifiers.extended_inv = Idstring("extended_inv") -end) - -function ExtendedInv.do_populate_extended_inventory(self, data) - - local psuccess, perror = pcall(function() - - local new_data = {} - local guis_catalog = "guis/" - local index = 0 - - for i, item_data in pairs( ExtendedInv:GetAllItems() ) do - - local hide = item_data.hide_when_none_in_stock or false - if ExtendedInv:ItemIsRegistered(i) and (hide == false or (hide == true and item_data.amount > 0)) then - - local item_id = item_data.id - local name_id = item_data.name - local desc_id = item_data.desc - local texture_id = item_data.texture - - index = index + 1 - new_data = {} - new_data.name = item_id - new_data.name_localized = managers.localization:text( name_id ) - new_data.desc_localized = managers.localization:text( desc_id ) - new_data.category = "extended_inv" - new_data.slot = index - new_data.amount = item_data.amount - new_data.unlocked = (new_data.amount or 0) > 0 - new_data.level = item_data.level or 0 - new_data.skill_based = new_data.level == 0 - new_data.skill_name = new_data.level == 0 and "bm_menu_skill_locked_" .. new_data.name - new_data.bitmap_texture = texture_id - new_data.lock_texture = self:get_lock_icon(new_data) - data[index] = new_data - - end - - end - - -- Fill empty slots - local max_items = data.override_slots[1] * data.override_slots[2] - for i = 1, max_items do - if not data[i] then - new_data = {} - new_data.name = "empty" - new_data.name_localized = "" - new_data.desc_localized = "" - new_data.category = "extended_inv" - new_data.slot = i - new_data.unlocked = true - new_data.equipped = false - data[i] = new_data - end - end - - end) - if not psuccess then - Print("[Error] " .. perror) - end - -end - -Hooks:Add("BlackMarketGUIStartPageData", "BlackMarketGUIStartPageData_ExtendedInventory", function(gui, data) - - local psuccess, perror = pcall(function() - - local should_hide_tab = true - for k, v in pairs( ExtendedInv:GetAllItems() ) do - if v.hide_when_none_in_stock == false or (v.hide_when_none_in_stock == true and v.amount > 0) then - should_hide_tab = false - end - end - if should_hide_tab then - return - end - - gui.populate_extended_inventory = ExtendedInv.do_populate_extended_inventory - - table.insert(data, { - name = "bm_menu_extended_inv", - category = "extended_inv", - on_create_func_name = "populate_extended_inventory", - identifier = gui.identifiers.extended_inv, - override_slots = {5, 2}, - start_crafted_item = 1 - }) - - end) - if not psuccess then - Print("[Error] " .. perror) - end - -end) - -Hooks:Add("BlackMarketGUIUpdateInfoText", "BlackMarketGUIUpdateInfoText_ExtendedInventory", function(gui) - - local psuccess, perror = pcall(function() - - local self = gui - local slot_data = self._slot_data - local tab_data = self._tabs[self._selected]._data - local prev_data = tab_data.prev_node_data - local ids_category = Idstring(slot_data.category) - local identifier = tab_data.identifier - local updated_texts = { - {text = ""}, - {text = ""}, - {text = ""}, - {text = ""}, - {text = ""} - } - - if ids_category == self.identifiers.extended_inv then - - updated_texts[1].text = slot_data.name_localized or "" - updated_texts[2].text = tostring(slot_data.amount or 0) .. " " .. ExtendedInv:GetReserveText(slot_data.name) - updated_texts[4].text = slot_data.desc_localized or "" - - gui:_update_info_text(slot_data, updated_texts) - - end - - end) - if not psuccess then - Print("[Error] " .. perror) - end - -end) - --- Saving and Loading -function ExtendedInv:GetSaveString() - - local contents = ""; - for k, v in pairs( ExtendedInv.Items ) do - - if type(v) == "table" then - contents = string.format( "%s[%s]\n", contents, tostring(k) ) - for a, b in pairs( v ) do - contents = string.format( "%s%s=%s\n", contents, tostring(a), tostring(b) ) - end - end - - end - - return contents - -end - -function ExtendedInv:Save(fileName) - - if fileName == nil then - fileName = ExtendedInv.SaveFile - end - - -- Encode data using "base64" while saving - -- Simple, but will stop most players from editing the save file since it'll look like gibberish - -- Those who want to cheat that badly will decode any encryption or use lua to bypass it, so - -- no point in super-complicated systems - local file = io.open(fileName, "w+") - local data = ExtendedInv:GetSaveString() - data = GoonBase.Utils.Base64:Encode( data ) - file:write( data ) - file:close() - -end - -function ExtendedInv:Load(fileName) - - if fileName == nil then - fileName = ExtendedInv.SaveFile - end - - local file = io.open(fileName, 'r') - local key - - if file == nil then - Print( "Could not open file (" .. fileName .. ")! Does it exist?" ) - return - end - - local fileString = "" - for line in file:lines() do - fileString = fileString .. line .. "\n" - end - fileString = GoonBase.Utils.Base64:Decode( fileString ) - - local fileLines = string.split(fileString, "[\n]") - for i, line in pairs( fileLines ) do - - local loadKey = line:match('^%[([^%[%]]+)%]$') - if loadKey then - key = tonumber(loadKey) and tonumber(loadKey) or loadKey - ExtendedInv.Items[key] = ExtendedInv.Items[key] or {} - end - - local param, val = line:match('^([%w|_]+)%s-=%s-(.+)$') - if param and val ~= nil then - - if tonumber(val) then - val = tonumber(val) - elseif val == "true" then - val = true - elseif val == "false" then - val = false - end - - if tonumber(param) then - param = tonumber(param) - end - - ExtendedInv.Items[key][param] = val - - end - - end - - file:close() - -end --- END OF FILE diff --git a/GoonBase/mods/gage_coins.lua b/GoonBase/mods/gage_coins.lua deleted file mode 100644 index 294e55d..0000000 --- a/GoonBase/mods/gage_coins.lua +++ /dev/null @@ -1,83 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - --- Mod Definition -local Mod = class( BaseMod ) -Mod.id = "GageCoins" -Mod.Name = "Gage-Coins" -Mod.Desc = "Gage has started up his own currency. For every courier assignment you complete, you'll get a whole Gage-Coin from the big man himself" -Mod.Requirements = { "ExtendedInventory" } -Mod.Incompatibilities = {} - -Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() - GoonBase.Mods:RegisterMod( Mod ) -end) - -if not Mod:IsEnabled() then - return -end - --- Mod Data -_G.GoonBase.GageCoins = _G.GoonBase.GageCoins or {} -local GageCoins = _G.GoonBase.GageCoins -local ExtendedInv = _G.GoonBase.ExtendedInventory - -GageCoins.CoinID = "gage_coin" - --- Localization -local Localization = GoonBase.Localization -Localization.GageCoinName = "Gage-Coin" -Localization.GageCoinDesc = [[A single coin of an electronic cryptro-currency designed by Gage himself. When he launched it though, it fell flat and became worthless. However with the rise of Crime.net he's has been selling them to heisters at super inflated prices. - -Gage gives one of these to every person who brings him a complete courier assignment.]] -Localization.GageCoinReserve = " IN WALLET" - --- Hooks -Hooks:Add("ExtendedInventoryInitialized", "ExtendedInventoryInitialized_" .. Mod:ID(), function() - - if ExtendedInv == nil then - return - end - - ExtendedInv:RegisterItem({ - id = GageCoins.CoinID, - name = "GageCoinName", - desc = "GageCoinDesc", - reserve_text = "GageCoinReserve", - texture = "guis/textures/pd2/blackmarket/icons/cash", - hide_when_none_in_stock = true, - }) - -end) - -Hooks:Add("GageAssignmentManagerOnMissionCompleted", "GageAssignmentManagerOnMissionCompleted_" .. Mod:ID(), function(assignment_manager) - - if ExtendedInv == nil then - return - end - - local self = assignment_manager - local total_pickup = 0 - - if self._progressed_assignments then - for assignment, value in pairs(self._progressed_assignments) do - - if value > 0 then - - local collected = Application:digest_value(self._global.active_assignments[assignment], false) + value - local to_aquire = self._tweak_data:get_value(assignment, "aquire") or 1 - while collected >= to_aquire do - collected = collected - to_aquire - ExtendedInv:AddItem(GageCoins.CoinID, 1) - end - - end - - total_pickup = total_pickup + value - end - end - -end) --- END OF FILE diff --git a/GoonBase/mods/grenade_indicator.lua b/GoonBase/mods/grenade_indicator.lua deleted file mode 100644 index f532974..0000000 --- a/GoonBase/mods/grenade_indicator.lua +++ /dev/null @@ -1,80 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - --- Mod Definition -local Mod = class( BaseMod ) -Mod.id = "GrenadeIndicator" -Mod.Name = "Grenade Indicator" -Mod.Desc = "Show an indicator on your HUD as an enemy grenade is thrown" -Mod.Requirements = {} -Mod.Incompatibilities = {} - -Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() - GoonBase.Mods:RegisterMod( Mod ) -end) - -if not Mod:IsEnabled() then - return -end - --- Localization -local Localization = GoonBase.Localization -Localization.OptionsMenu_GrenadeMarker = "Show Markers on Flashbangs" -Localization.OptionsMenu_GrenadeMarkerDesc = "Show a HUD marker when a flashbang is deployed" - --- Options -GoonBase.Options.GrenadeIndicator = GoonBase.Options.GrenadeIndicator or {} -GoonBase.Options.GrenadeIndicator.ShowGrenadeMarker = true - -local WaypointName = "GoonBaseGrenadeWaypoint_" - --- Hooks -Hooks:Add( "QuickSmokeGrenadeActivate", "QuickSmokeGrenadeActivate_GrenadeIndicator", function( this, pos, duration ) - - if GoonBase.Options.GrenadeIndicator.ShowGrenadeMarker then - - this.grenadeID = tostring(math.random(0, 10000)) - managers.hud:add_waypoint( - WaypointName .. this.grenadeID, - { icon = 'pd2_kill', distance = true, position = pos, no_sync = false, present_timer = 0, state = "present", radius = 100, color = Color.yellow, blend_mode = "add" } - ) - - end - -end ) - -Hooks:Add( "QuickSmokeGrenadeDetonate", "QuickSmokeGrenadeDetonate_GrenadeIndicator", function( this ) - - if GoonBase.Options.GrenadeIndicator.ShowGrenadeMarker then - managers.hud:remove_waypoint( WaypointName .. this.grenadeID ) - end - -end ) - --- Add options to menu -Hooks:Add("MenuManagerSetupGoonBaseMenu", "MenuManagerSetupGoonBaseMenu_GrenadeIndicator", function( menu_manager ) - - local success, err = pcall(function() - - MenuCallbackHandler.toggle_grenade_marker = function(this, item) - GoonBase.Options.GrenadeIndicator.ShowGrenadeMarker = item:value() == "on" and true or false - GoonBase.Options:Save() - end - - GoonBase.MenuHelper:AddToggle({ - id = "toggle_grenade_marker", - title = "OptionsMenu_GrenadeMarker", - desc = "OptionsMenu_GrenadeMarkerDesc", - callback = "toggle_grenade_marker", - value = GoonBase.Options.GrenadeIndicator.ShowGrenadeMarker, - menu_id = "goonbase_options_menu", - priority = -1 - }) - - end) - if not success then PrintTable(err) end - -end) --- END OF FILE diff --git a/GoonBase/mods/mod_shop.lua b/GoonBase/mods/mod_shop.lua deleted file mode 100644 index 6ddd593..0000000 --- a/GoonBase/mods/mod_shop.lua +++ /dev/null @@ -1,531 +0,0 @@ ----------- --- Payday 2 GoonMod, Public Release Beta 2, built on 1/9/2015 9:30:33 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - --- Mod Definition -local Mod = class( BaseMod ) -Mod.id = "BlackmarketModShop" -Mod.Name = "Gage's Mod Shop" -Mod.Desc = "Gage will sell you weapon parts, masks, and mask customization items in return for Gage Coins" -Mod.Requirements = { "ExtendedInventory", "GageCoins" } -Mod.Incompatibilities = {} - -Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() - GoonBase.Mods:RegisterMod( Mod ) -end) - -if not Mod:IsEnabled() then - return -end - --- Mod Shop -_G.GoonBase.ModShop = _G.GoonBase.ModShop or {} -local ModShop = _G.GoonBase.ModShop -local ExtendedInv = _G.GoonBase.ExtendedInventory - -ModShop.PurchaseCurrency = "gage_coin" -ModShop.CostRegular = 1 -ModShop.CostInfamous = 3 - -ModShop.ExclusionList = { - ["nothing"] = true, - ["no_color_no_material"] = true, - ["no_color_full_material"] = true, - ["plastic"] = true, - ["character_locked"] = true, -} - -ModShop.DLCAlwaysUnlocked = { - ["halloween"] = true, -} - -ModShop.MaskAllowanceList = { - ["normal"] = true, - ["halloween"] = true, - ["infamous"] = true, -} - -ModShop.MaskPricing = { - ["default"] = 5, - ["dlc"] = 5, - ["normal"] = 5, - ["pd2_clan"] = 3, - ["halloween"] = 8, - ["infamous"] = 20, - ["infamy"] = 10, -} - -ModShop.MaskModAllowanceList = { - ["normal"] = true, - ["halloween"] = true, - ["infamous"] = true, -} - --- Localization -local Localization = GoonBase.Localization -Localization.ModShop_BlackmarketPurchaseWithGageCoins = "Purchase with Gage Coins" -Localization.ModShop_PurchaseWindowTitle = "Purchase" -Localization.ModShop_PurchaseWindowMessage = [[You are about to purchase {1}. This will cost you {2} Gage Coin/s. - -Purchasing: - {1}, {2} GC/s -Balance before purchase: - {3} GC -Balance after purchase: - {4} GC]] -Localization.ModShop_PurchaseWindowAccept = "Purchase" -Localization.ModShop_PurchaseWindowCancel = "Cancel" -Localization.ModShop_FreeOfChargeTitle = "Cannot Purchase" -Localization.ModShop_FreeOfChargeMessage = "{1} is free of charge and can be applied to as many weapons as you wish." -Localization.ModShop_FreeOfChargeAccept = "OK" -Localization.ModShop_NotEnoughCoinsWindowTitle = "Cannot Purchase" -Localization.ModShop_NotEnoughCoinsWindowMessage = "You cannot purchase {1}, as you do not have enough Gage Coins to afford it. To purchase {1}, you need {2} GC/s." -Localization.ModShop_NotEnoughCoinsWindowAccept = "OK" - --- Blackmarket Menu -Hooks:Add("BlackMarketGUIPostSetup", "BlackMarketGUIPostSetup_" .. Mod:ID(), function(gui, is_start_page, component_data) - - Hooks:RegisterHook("ModShopAttemptPurchaseWeaponMod") - gui.modshop_purchase_weaponmod_callback = function(self, data) - Hooks:Call("ModShopAttemptPurchaseWeaponMod", data) - end - - Hooks:RegisterHook("ModShopAttemptPurchaseMask") - gui.modshop_purchase_mask_callback = function(self, data) - Hooks:Call("ModShopAttemptPurchaseMask", data) - end - - Hooks:RegisterHook("ModShopAttemptPurchaseMaskPart") - gui.modshop_purchase_mask_part_callback = function(self, data) - Hooks:Call("ModShopAttemptPurchaseMaskPart", data) - end - - local wm_modshop = { - prio = 5, - btn = "BTN_BACK", - pc_btn = Idstring("toggle_chat"), - name = "ModShop_BlackmarketPurchaseWithGageCoins", - callback = callback(gui, gui, "modshop_purchase_weaponmod_callback") - } - - local bm_modshop = { - prio = 5, - btn = "BTN_BACK", - pc_btn = Idstring("toggle_chat"), - name = "ModShop_BlackmarketPurchaseWithGageCoins", - callback = callback(gui, gui, "modshop_purchase_mask_callback") - } - - local mp_modshop = { - prio = 5, - btn = "BTN_BACK", - pc_btn = Idstring("toggle_chat"), - name = "ModShop_BlackmarketPurchaseWithGageCoins", - callback = callback(gui, gui, "modshop_purchase_mask_part_callback") - } - - local btn_x = 10 - gui._btns["wm_modshop"] = BlackMarketGuiButtonItem:new(gui._buttons, wm_modshop, btn_x) - gui._btns["bm_modshop"] = BlackMarketGuiButtonItem:new(gui._buttons, bm_modshop, btn_x) - gui._btns["mp_modshop"] = BlackMarketGuiButtonItem:new(gui._buttons, mp_modshop, btn_x) - -end) - -Hooks:Add("BlackMarketGUIOnPopulateModsActionList", "BlackMarketGUIOnPopulateModsActionList_" .. Mod:ID(), function(gui, data) - if ModShop:WeaponModAllowed(data) then - table.insert(data, "wm_modshop") - end -end) - -function ModShop:WeaponModAllowed(mod) - - if mod == nil then - return false - end - - if mod.free_of_charge then - return false - end - - for k, v in pairs( tweak_data.dlc ) do - if v.achievement_id ~= nil and v.content ~= nil and v.content.loot_drops ~= nil then - for i, loot in pairs( v.content.loot_drops ) do - if loot.item_entry ~= nil and loot.item_entry == mod.name then - return managers.achievment.handler:has_achievement(v.achievement_id) - end - end - end - end - - local gv = mod.global_value - if gv == nil or gv == "normal" then - return true - end - - if not managers.dlc:is_dlc_unlocked(gv) then - return false - end - - return true - -end - -Hooks:Add("BlackMarketGUIOnPopulateBuyMasksActionList", "BlackMarketGUIOnPopulateBuyMasksActionList_" .. Mod:ID(), function(gui, data) - if ModShop:IsMaskOrModAllowed(data, ModShop.MaskAllowanceList) then - table.insert(data, "bm_modshop") - end -end) - -Hooks:Add("BlackMarketGUIOnPopulateMaskModsActionList", "BlackMarketGUIOnPopulateMaskModsActionList_" .. Mod:ID(), function(gui, data) - if ModShop:IsMaskOrModAllowed(data, ModShop.MaskModAllowanceList) then - table.insert(data, "mp_modshop") - end -end) - -function ModShop:IsMaskOrModAllowed(mod, allowance_list) - - if mod == nil then - return false - end - - local gv = mod.global_value - if gv == nil then - return true - end - - if ModShop.ExclusionList[mod.name] == true or ModShop.ExclusionList[gv] == true then - return false - end - - for k, v in pairs( tweak_data.dlc ) do - if v.achievement_id ~= nil and v.content ~= nil and v.content.loot_drops ~= nil then - for i, loot in pairs( v.content.loot_drops ) do - if loot.item_entry ~= nil and loot.item_entry == mod.name then - return managers.achievment.handler:has_achievement(v.achievement_id) - end - end - end - end - - local infamy_lock = mod.infamy_lock - if infamy_lock ~= nil or gv == "infamy" then - local is_unlocked = managers.infamy:owned(infamy_lock) or infamy_lock == nil - if not is_unlocked then - return false - end - end - - if allowance_list and allowance_list[gv] then - return true - end - - if gv ~= "infamy" and not managers.dlc:is_dlc_unlocked(gv) then - return false - end - - return true - -end - -Hooks:Add("BlackMarketManagerModifyGetInventoryCategory", "BlackMarketManagerModifyGetInventoryCategory_" .. Mod:ID(), function(blackmarket, category, data) - - for k, v in pairs( tweak_data.blackmarket[category] ) do - - local already_in_table = false - for x, y in pairs( data ) do - if y.id == k then - already_in_table = true - end - end - - local gv = v.dlc or v.global_value or "normal" - if not already_in_table and ModShop.ExclusionList[k] ~= true then - - if v.infamous then - gv = "infamous" - end - - if gv == "normal" or gv == "infamous" or ( (gv ~= "normal" and managers.dlc:is_dlc_unlocked(gv)) or ModShop.DLCAlwaysUnlocked[gv] == true ) then - table.insert(data, { - id = k, - global_value = gv, - amount = 0 - }) - end - - end - - end - -end) - --- Purchase Hooks -Hooks:Add("ModShopAttemptPurchaseWeaponMod", "ModShopAttemptPurchaseWeaponMod_" .. Mod:ID(), function(data) - ModShop:SetWeaponModPurchaseData(data) - ModShop:ShowPurchaseMenu() -end) - -Hooks:Add("ModShopAttemptPurchaseMask", "ModShopAttemptPurchaseMask_" .. Mod:ID(), function(data) - ModShop:SetMaskPurchaseData(data) - ModShop:ShowPurchaseMenu() -end) - -Hooks:Add("ModShopAttemptPurchaseMaskPart", "ModShopAttemptPurchaseMaskPart_" .. Mod:ID(), function(data) - ModShop:SetMaskPartPurchaseData(data) - ModShop:ShowPurchaseMenu() -end) - --- Purchase Menu -function ModShop:SetPurchaseData( data ) - - self._purchase_data = {} - self._purchase_data.name = data.name - self._purchase_data.name_localized = data.name_localized - self._purchase_data.category = data.category - self._purchase_data.global_value = data.global_value - self._purchase_data.cost = ModShop.CostRegular - -end - -function ModShop:SetWeaponModPurchaseData( data ) - - local psuccess, perror = pcall(function() - - self:SetPurchaseData( data ) - - if self:IsWeaponMod( data.category ) then - if data.free_of_charge ~= nil and data.free_of_charge == true then - self._purchase_data.free_of_charge = true - end - end - - end) - if not psuccess then - Print("[Error] " .. perror) - end - -end - -function ModShop:SetMaskPurchaseData( data ) - - local psuccess, perror = pcall(function() - - self:SetPurchaseData( data ) - - if self:IsMask( data.category ) then - - local price = ModShop.MaskPricing[ data.global_value ] or ModShop.MaskPricing["default"] - if data.dlc ~= nil then - price = ModShop.MaskPricing["dlc"] - end - if data.infamy_lock ~= nil then - price = ModShop.MaskPricing["infamy"] - end - - self._purchase_data.cost = price - - end - - end) - if not psuccess then - Print("[Error] " .. perror) - end - -end - -function ModShop:SetMaskPartPurchaseData( data ) - - local psuccess, perror = pcall(function() - - self:SetPurchaseData( data ) - - if self:IsMaskPart( data.category ) then - - local mod_data = tweak_data.blackmarket[data.category][data.name] - if mod_data ~= nil then - - if mod_data.infamous ~= nil and mod_data.infamous == true then - self._purchase_data.cost = ModShop.CostInfamous - end - - if mod_data.global_value == "infamy" or mod_data.infamy_lock ~= nil then - self._purchase_data.cost = ModShop.CostInfamous - end - - end - - end - - end) - if not psuccess then - Print("[Error] " .. perror) - end - -end - -function ModShop:ShowPurchaseMenu() - - if not ExtendedInv then - Print("[Error] Attempting to show purchase menu with no Extended Inventory...") - return - end - - local gage_coins = ExtendedInv:GetItem( ModShop.PurchaseCurrency ) - local purchase_cost = self._purchase_data.cost - - if not gage_coins.amount or type(gage_coins.amount) == "string" then - Print("[Error] Attempting to show purchase menu with no coin amount, or a string as the amount") - return - end - if not purchase_cost or type(purchase_cost) == "string" then - Print("[Error] Attmpting to show purchase menu with a string as the purchasing cost") - return - end - - -- Check if item is free of charge - if self._purchase_data.free_of_charge ~= nil and self._purchase_data.free_of_charge == true then - self:ShowFreeOfCharge() - return - end - - -- Check if we can afford this - if gage_coins.amount < purchase_cost or purchase_cost <= 0 then - self:ShowNotEnoughCoins( purchase_cost ) - return - end - - -- Show purchase menu - local title = managers.localization:text("ModShop_PurchaseWindowTitle") - local message = managers.localization:text("ModShop_PurchaseWindowMessage") - message = message:gsub("{1}", self._purchase_data.name_localized) - message = message:gsub("{2}", purchase_cost) - message = message:gsub("{3}", gage_coins.amount) - message = message:gsub("{4}", gage_coins.amount - purchase_cost) - - local menuOptions = {} - menuOptions[1] = { - text = managers.localization:text("ModShop_PurchaseWindowAccept"), - callback = ModShop.PurchaseItem - } - menuOptions[2] = { - text = managers.localization:text("ModShop_PurchaseWindowCancel"), - callback = nil, - is_cancel_button = true - } - local window = SimpleMenu:New(title, message, menuOptions) - window:Show() - -end - -function ModShop:ShowFreeOfCharge() - - local title = managers.localization:text("ModShop_FreeOfChargeTitle") - local message = managers.localization:text("ModShop_FreeOfChargeMessage") - message = message:gsub("{1}", self._purchase_data.name_localized) - local menuOptions = {} - menuOptions[1] = { - text = managers.localization:text("ModShop_FreeOfChargeAccept"), - is_cancel_button = true - } - local window = SimpleMenu:New(title, message, menuOptions) - window:Show() - -end - -function ModShop:ShowNotEnoughCoins(cost) - - local title = managers.localization:text("ModShop_NotEnoughCoinsWindowTitle") - local message = managers.localization:text("ModShop_NotEnoughCoinsWindowMessage") - message = message:gsub("{1}", self._purchase_data.name_localized) - message = message:gsub("{2}", cost) - local menuOptions = {} - menuOptions[1] = { - text = managers.localization:text("ModShop_NotEnoughCoinsWindowAccept"), - is_cancel_button = true - } - local window = SimpleMenu:New(title, message, menuOptions) - window:Show() - -end - -function ModShop:PurchaseItem() - - local psuccess, perror = pcall(function() - - if not ExtendedInv then - Print("[Error] Attempting to purchase item with no Extended Inventory...") - return - end - - local purchase_data = ModShop._purchase_data - local item = purchase_data.name - local category = purchase_data.category - local cost = purchase_data.cost - local global_value = purchase_data.global_value - - Print("Purchasing ", item, " from category ", category, " at cost: ", cost, " coins") - - -- Add to weapon inventory - if ModShop:IsWeaponMod(category) then - managers.blackmarket:add_to_inventory(global_value, "weapon_mods", item, true) - ModShop:ReloadBlackMarketAfterPurchase() - end - - -- Add to mask inventory - if ModShop:IsMaskPart(category) then - - managers.blackmarket:add_traded_mask_part_to_inventory(item, category) - - -- Temporary measure to reload mask mods inventory - local blackmarket_gui = managers.menu_component._blackmarket_gui - if blackmarket_gui then - blackmarket_gui:_abort_customized_mask_callback() - end - - end - - -- Add mask to inventory - if ModShop:IsMask(category) then - managers.blackmarket:add_to_inventory(global_value, "masks", item, true) - ModShop:ReloadBlackMarketAfterPurchase() - end - - -- Remove coins - ExtendedInv:TakeItem( ModShop.PurchaseCurrency, cost ) - - end) - if not psuccess then - Print("[Error] " .. perror) - end - -end - -function ModShop:ReloadBlackMarketAfterPurchase() - local blackmarket_gui = managers.menu_component._blackmarket_gui - if blackmarket_gui then - blackmarket_gui:reload() - end -end - -function ModShop:IsWeaponMod(category) - if category == "primaries" or category == "secondaries" then - return true - end - return false -end - -function ModShop:IsMask(category) - if category == "masks" then - return true - end - return false -end - -function ModShop:IsMaskPart(category) - if category == "colors" or category == "textures" or category == "materials" then - return true - end - return false -end --- END OF FILE diff --git a/GoonBase/mods/mutators.lua b/GoonBase/mods/mutators.lua deleted file mode 100644 index 8594206..0000000 --- a/GoonBase/mods/mutators.lua +++ /dev/null @@ -1,900 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 1/3/2015 12:28:05 AM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - --- Mod Definition -local Mod = class( BaseMod ) -Mod.id = "GameplayMutators" -Mod.Name = "Mutators" -Mod.Desc = "Micro-gameplay mods that give you new gameplay modes and experiences" -Mod.Requirements = {} -Mod.Incompatibilities = {} - -Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() - GoonBase.Mods:RegisterMod( Mod ) -end) - -if not Mod:IsEnabled() then - return -end - --- Mutators -_G.GoonBase.Mutators = _G.GoonBase.Mutators or {} -local Mutators = _G.GoonBase.Mutators -Mutators.MenuID = "goonbase_mutators_menu" -Mutators.MatchmakingData = "gb_mutators" -Mutators.LoadedMutators = Mutators.LoadedMutators or {} -Mutators.ActiveMutators = Mutators.ActiveMutators or {} -Mutators.ClientMutatorCheck = Mutators.ClientMutatorCheck or {} -Mutators.NetworkTimeoutTime = 3 - --- Network -Mutators.Network = {} -Mutators.Network.ClearMutators = "ClearMutators" -Mutators.Network.EnableMutator = "EnableMutator" -Mutators.Network.DisableMutator = "DisableMutator" -Mutators.Network.MutatorCheck = "CheckMutator" -Mutators.Network.MutatorCheckSuccess = "CheckMutatorSuccess" -Mutators.Network.MutatorCheckFailure = "CheckMutatorFailure" - --- Paths -Mutators.MutatorsPath = "/" -Mutators.MutatorsList = { - "mutators/base_mutator.lua", - "mutators/mutator_addicts.lua", - "mutators/mutator_all_bulldozers.lua", - "mutators/mutator_all_cloakers.lua", - "mutators/mutator_all_shields.lua", - "mutators/mutator_all_tazers.lua", - "mutators/mutator_exploding_bullets.lua", - "mutators/mutator_floating_bodies.lua", - "mutators/mutator_insane_spawnrate.lua", - "mutators/mutator_insane_spawnrate_cops.lua", - "mutators/mutator_instagib.lua", - "mutators/mutator_jamming_weapons.lua", - "mutators/mutator_lightning_speed.lua", - "mutators/mutator_no_ammo_pickups.lua", - "mutators/mutator_realism_mode.lua", - "mutators/mutator_shielddozers.lua", - "mutators/mutator_suicidal_spawnrate.lua", - "mutators/mutator_suicidal_spawnrate_cops.lua", - "mutators/mutator_suicide_cloakers.lua", - "mutators/mutator_unbreakable.lua", -} -Mutators.MenuPrefix = "toggle_mutator_" - --- Localization -local Localization = GoonBase.Localization -Localization.menu_mutators = "Mutators" -Localization.Mutators_OptionsName = "Mutators" -Localization.Mutators_OptionsDesc = "Control active gameplay Mutators" -Localization.Mutators_OptionsIngameName = "Mutators" -Localization.Mutators_OptionsIngameDesc = "Control active gameplay Mutators (Changes will take place on a restart/new day)" -Localization.Mutators_IncompatibleTitle = "Mutators" -Localization.Mutators_IncompatibleMessage = "'{1}' could not be enabled as it is in compatible with {2}. Please disable these mutators first." -Localization.Mutators_IncompatibleAccept = "OK" - -Localization.Mutators_HelpButton = "Help" -Localization.Mutators_HelpButtonDesc = "Show the mutators menu help" -Localization.Mutators_HelpTitle = "Mutators" -Localization.Mutators_HelpMessage = [[This menu allows you to enable and disable specific gameplay mutators. Mutators are small gameplay modifications that can offer new gameplay modes, and unique experiences. - -Certain mutators may be incompatible with other mutators, and will turn red when an incompatible mutator is active. In order to use this mutator, you must first disable the incompatible mutator. - -Mutators are only active in certain circumstances, and in order to prevent griefing in public games, mutators are disabled in all games except for friends-only, and private games. - -Mutators will also disabled ALL achievements while they are active. If you are achievement hunting, disable all of your mutators first. -]] -Localization.Mutators_HelpAccept = "Close" -Localization.Randomizer_Name = "Randomizer" -Localization.Randomizer_Desc = [[Randomly selects mutations that are compatible with each other to be activated at the start of every heist -Any mutations that you have turned on will also be enabled]] -Localization.Randomizer_Off = "Off" -Localization.Randomizer_UpTo1 = "Single Mutation" -Localization.Randomizer_UpTo2 = "Up to 2 Mutations" -Localization.Randomizer_UpTo3 = "Up to 3 Mutations" -Localization.Randomizer_UpTo4 = "Up to 4 Mutations" -Localization.Randomizer_UpTo5 = "Up to 5 Mutations" - -Localization.Mutators_PublicGamesWarning_Title = "Public Lobby Disabled" -Localization.Mutators_PublicGamesWarning_Message = "Cannot make lobby public as mutators are active. If you wish to host a public game, please disable your mutators first." -Localization.Mutators_PublicGamesWarning_MessageIngame = "Cannot make lobby public as mutators have been activated. If you wish to host a public game, please return to the lobby and disable your mutators." -Localization.Mutators_PublicGamesWarning_Cancel = "OK" - -Localization.NetworkedMutators_SendingData_Title = "Sending Mutators" -Localization.NetworkedMutators_SendingData_Message = "Sending mutator data to other players, please wait..." -Localization.NetworkedMutators_SendingData_Cancel = "Cancel" -Localization.BriefingMenu_ActiveMutators = "Active Mutations" -Localization.MissingMutators_Title = "Missing Mutators" -Localization.MissingMutators_Message = "Some players in the lobby are missing mutators you are trying to activate, they have been listed below." -Localization.MissingMutators_Continue = "Continue Anyway" -Localization.MissingMutators_Cancel = "Cancel" - --- Options -if GoonBase.Options.Mutators == nil then - GoonBase.Options.Mutators = {} - GoonBase.Options.Mutators.RandomizerMode = 1 -end - --- Add mutators menu -Hooks:Add("MenuManagerSetupCustomMenus", "MenuManagerSetupCustomMenus_MutatorsMenu", function(menu_manager, menu_nodes) - - -- Create menu - GoonBase.MenuHelper:NewMenu( Mutators.MenuID ) - - -- Add help button - MenuCallbackHandler.open_mutators_menu_help = function(this, item) - Mutators:ShowHelpMenu() - end - - MenuCallbackHandler.mutators_set_randomizer_mode = function(this, item) - GoonBase.Options.Mutators.RandomizerMode = tonumber(item:value()) - GoonBase.Options:Save() - end - - GoonBase.MenuHelper:AddButton({ - id = "goonbase_mutators_menu_help_button", - title = "Mutators_HelpButton", - desc = "Mutators_HelpButtonDesc", - callback = "open_mutators_menu_help", - menu_id = Mutators.MenuID, - priority = 1003, - }) - - GoonBase.MenuHelper:AddDivider({ - id = "goonbase_mutators_menu_help_divider", - menu_id = Mutators.MenuID, - size = 8, - priority = 1002, - }) - - GoonBase.MenuHelper:AddMultipleChoice({ - id = "goonbase_mutators_menu_randomizer", - title = "Randomizer_Name", - desc = "Randomizer_Desc", - callback = "mutators_set_randomizer_mode", - menu_id = Mutators.MenuID, - priority = 1001, - items = { - [1] = "Randomizer_Off", - [2] = "Randomizer_UpTo1", - [3] = "Randomizer_UpTo2", - [4] = "Randomizer_UpTo3", - [5] = "Randomizer_UpTo4", - [6] = "Randomizer_UpTo5", - }, - value = GoonBase.Options.Mutators.RandomizerMode or 1, - }) - - GoonBase.MenuHelper:AddDivider({ - id = "goonbase_mutators_menu_help_divider2", - menu_id = Mutators.MenuID, - size = 8, - priority = 1000, - }) - - - -- Add mutators to menu - Mutators:AddLoadedMutatorsToMenu() - -end) - -Hooks:Add("MenuManagerBuildCustomMenus", "MenuManagerBuildCustomMenus_MutatorsMenu", function(menu_manager, menu_nodes) - - -- Build menu - local menu_id = Mutators.MenuID - menu_nodes[menu_id] = GoonBase.MenuHelper:BuildMenu( menu_id ) - - -- Add to main menu and lobby only - if menu_nodes.main ~= nil then - GoonBase.MenuHelper:AddMenuItem( menu_nodes.main, menu_id, "Mutators_OptionsName", "Mutators_OptionsDesc", "safehouse", "after" ) - end - if menu_nodes.lobby ~= nil then - GoonBase.MenuHelper:AddMenuItem( menu_nodes.lobby, menu_id, "Mutators_OptionsName", "Mutators_OptionsDesc", "skilltree", "after" ) - end - - -- Add to ingame menu - if menu_nodes.pause ~= nil then - GoonBase.MenuHelper:AddMenuItem( menu_nodes.pause, menu_id, "Mutators_OptionsIngameName", "Mutators_OptionsIngameDesc", "options", "after" ) - end - - -- Verify incompatibilities - Mutators:VerifyAllIncompatibilities() - -end) - --- Mutators Functions -function Mutators:ShowHelpMenu() - - local title = managers.localization:text("Mutators_HelpTitle") - local message = managers.localization:text("Mutators_HelpMessage") - local menu_options = {} - menu_options[1] = { text = managers.localization:text("Mutators_HelpAccept"), is_cancel_button = true } - local tradeMenu = SimpleMenu:New(title, message, menu_options) - tradeMenu:Show() - -end - -function Mutators:LoadMutators() - - for k, v in pairs( self.MutatorsList ) do - SafeDoFile( GoonBase.Path .. self.MutatorsPath .. v ) - end - -end - -function Mutators:RegisterMutator(mutator) - Print("[Mutators] Registering mutator '" .. mutator:ID() .. "'") - self.LoadedMutators[ mutator:ID() ] = mutator -end - -function Mutators:SetupMutatorsLocalization() - for k, v in pairs( Mutators.LoadedMutators ) do - v:SetupLocalization() - end -end - -function Mutators:SetupMutators() - - if Global.game_settings and Global.game_settings.active_mutators then - - for k, v in pairs( Global.game_settings.active_mutators ) do - if v and Mutators.LoadedMutators[k] then - Mutators.LoadedMutators[k]:Setup() - end - end - - return - else - - for k, v in pairs( Mutators.LoadedMutators ) do - v:Setup() - end - - end - -end - -function Mutators:AddLoadedMutatorsToMenu() - - for k, v in pairs( Mutators.LoadedMutators ) do - if v.HideInOptionsMenu ~= true then - v:SetupMenu() - end - end - -end - -function Mutators:VerifyAllIncompatibilities() - - for i, mutator in pairs( Mutators.LoadedMutators ) do - mutator:VerifyIncompatibilities() - end - -end - -function Mutators:CheckIncompatibilities( mutator ) - - local incompatible_list = mutator:IncompatibleMutators() - local incompatible = {} - local num_incompatibilities = 0 - - -- Find incompatible mutators - for k, v in pairs( Mutators.LoadedMutators ) do - if v:ShouldBeEnabled() and table.contains( incompatible_list, v:ID() ) then - incompatible[k] = true - num_incompatibilities = num_incompatibilities + 1 - end - end - - -- Mutators are all compatible - if num_incompatibilities < 1 then - return true - end - - -- Incompatible mutators - return incompatible - -end - -function Mutators:ShowIncompatibilitiesWindow( mutator, incompatible ) - - -- Display incompatible mutators - local incompatible_str = "" - for k, v in pairs( incompatible ) do - - if incompatible_str ~= "" then - incompatible_str = incompatible_str .. ", " - end - incompatible_str = incompatible_str .. "'" .. Mutators.LoadedMutators[k]:GetLocalizedName() .. "'" - - end - - -- Display - local title = managers.localization:text("Mutators_IncompatibleTitle") - local message = managers.localization:text("Mutators_IncompatibleMessage") - message = message:gsub("{1}", mutator:GetLocalizedName()) - message = message:gsub("{2}", incompatible_str) - local menuOptions = {} - menuOptions[1] = { - text = managers.localization:text("Mutators_IncompatibleAccept"), - is_cancel_button = true - } - local menu = SimpleMenu:New(title, message, menuOptions) - menu:Show() - - return false - -end - -function Mutators:IsRandomizerEnabled() - return (GoonBase.Options.Mutators.RandomizerMode or 1) > 1 -end - -function Mutators:GetNumberOfRandomMutations() - return (GoonBase.Options.Mutators.RandomizerMode or 1) - 1 -end - -function Mutators:AddRandomizedMutations() - - if Mutators:IsRandomizerEnabled() then - - Print("[Mutators] Randomizing mutators, up to ", Mutators:GetNumberOfRandomMutations()) - - if Mutators.ILoadedMutators == nil then - Mutators.ILoadedMutators = {} - for k, v in pairs(Mutators.LoadedMutators) do - table.insert(Mutators.ILoadedMutators, k) - end - end - - local random_mutations = {} - - for i = 1, Mutators:GetNumberOfRandomMutations(), 1 do - - local mutation_id = nil - local attempts = 0 - while mutation_id == nil do - - mutation_id = Mutators.ILoadedMutators[ math.random(1, #Mutators.ILoadedMutators) ] - if random_mutations[mutation_id] or Mutators.ActiveMutators[mutation_id] or not Mutators.LoadedMutators[mutation_id]:VerifyIncompatibilities(true) then - mutation_id = nil - end - - attempts = attempts + 1 - if attempts > 16 then - mutation_id = nil - break - end - - end - - if mutation_id ~= nil then - local mutation = Mutators.LoadedMutators[mutation_id] - if mutation:VerifyIncompatibilities(true) then - mutation:ForceEnable() - end - end - - end - - end - -end - -function Mutators:GetNumberOfActiveMutators() - local i = 0 - for k, v in pairs( Mutators.ActiveMutators ) do - i = i + 1 - end - return i -end - -function Mutators:GetNumberOfMutatorsToBeActive() - - local i = 0 - - for k, v in pairs( Mutators.LoadedMutators ) do - if v:ShouldBeEnabled() then - i = i + 1 - end - end - - if self:IsRandomizerEnabled() then - i = i + self:GetNumberOfRandomMutations() - end - - return i - -end - -function Mutators:PrintActiveMutators() - Print("-----") - Print("Active Mutators:") - for k, v in pairs( Mutators.ActiveMutators ) do - Print(k) - end - Print("-----") -end - --- Hooks -Hooks:Add("AchievementManagerCheckDisable", "AchievementManagerCheckDisable_Mutators", function(achievement_manager) - - for k, v in pairs( Mutators.LoadedMutators ) do - if v:ShouldBeEnabled() then - achievement_manager:DisableAchievements("mutators") - end - end - - if Mutators:GetNumberOfActiveMutators() > 0 then - achievement_manager:DisableAchievements("mutators") - end - -end) - --- Load mutators -Hooks:RegisterHook("GoonBaseRegisterMutators") -Hooks:Add("GoonBasePostLoadedMods", "GoonBasePostLoadedMods_Mutators", function() - - Print("[Mutators] Loading Mutators") - Mutators:LoadMutators() - - Hooks:Call("GoonBaseRegisterMutators") - - Print("[Mutators] Setting up mutators") - Mutators:SetupMutatorsLocalization() - Mutators:SetupMutators() - -end) - --- Permission locking -Hooks:Add("PostCreateCrimenetContractGUI", "PostCreateCrimenetContractGUI_Mutators", function( menu, node, gui ) - - if Mutators:GetNumberOfMutatorsToBeActive() < 1 then - return - end - if not gui._node then - return - end - if not gui._node._items then - return - end - - local items = gui._node._items - for k, v in pairs( items ) do - if v._parameters.name == "lobby_permission" then - local permission = "friends_only" - v:set_value( permission ) - Global.game_settings.permission = permission - end - end - -end) - -Hooks:Add("MenuCallbackHandlerPreChoseLobbyPermission", "MenuCallbackHandlerPreChoseLobbyPermission_Mutators", function( callback_handler, item ) - return Mutators:ForcePrivateGamesForMutators( item ) -end) - -Hooks:Add( "MenuCallbackHandlerPreCrimeNetChoseLobbyPermission", "MenuCallbackHandlerPreCrimeNetChoseLobbyPermission_Mutators", function( callback_handler, item ) - return Mutators:ForcePrivateGamesForMutators( item ) -end ) - -function Mutators:ForcePrivateGamesForMutators( item ) - - if item:value() == "public" and Mutators:GetNumberOfMutatorsToBeActive() > 0 then - item:set_value( "friends_only" ) - if Mutators:IsInGame() then - Mutators:ShowPublicGamesWarningIngame() - return true - end - Mutators:ShowPublicGamesWarningLobby() - return true - end - -end - -function Mutators:ShowPublicGamesWarningLobby() - - local title = managers.localization:text("Mutators_PublicGamesWarning_Title") - local message = managers.localization:text("Mutators_PublicGamesWarning_Message") - local menu_options = {} - menu_options[1] = { text = managers.localization:text("Mutators_PublicGamesWarning_Cancel"), is_cancel_button = true } - local warning = SimpleMenu:New(title, message, menu_options) - warning:Show() - -end - -function Mutators:ShowPublicGamesWarningIngame() - - local title = managers.localization:text("Mutators_PublicGamesWarning_Title") - local message = managers.localization:text("Mutators_PublicGamesWarning_MessageIngame") - local menu_options = {} - menu_options[1] = { text = managers.localization:text("Mutators_PublicGamesWarning_Cancel"), is_cancel_button = true } - local warning = SimpleMenu:New(title, message, menu_options) - warning:Show() - -end - -function Mutators:IsInGame() - if not game_state_machine then - return false - end - return string.find( game_state_machine:current_state_name(), "ingame" ) -end - --- Network Mutators -Hooks:Add( "MenuCallbackHandlerPreStartTheGame", "MenuCallbackHandlerPreStartTheGame_Mutators", function( callback_handler ) - - Mutators.ActiveMutators = {} - for k, v in pairs( Mutators.LoadedMutators ) do - if v and v:ShouldBeEnabled() then - Mutators.ActiveMutators[k] = true - end - end - - if not GoonBase.Network:IsMultiplayer() or ( GoonBase.Network:IsMultiplayer() and GoonBase.Network:IsHost() ) then - Mutators:AddRandomizedMutations() - end - - if Global.game_settings then - - Global.game_settings.active_mutators = {} - for k, v in pairs( Mutators.ActiveMutators ) do - Global.game_settings.active_mutators[k] = v - end - - end - - if Mutators:CheckNetworkMutators(callback_handler) then - - callback_handler:delay_game_start( "MutatorNetworking" ) - - Mutators._game_delay_time = Application:time() - Mutators.ClientMutatorCheck = {} - - Mutators:ClearClientsNetworkedMutators() - Mutators:ShowNetworkingMutatorsWindow() - - for k, v in pairs( Mutators.ActiveMutators ) do - Mutators.ClientMutatorCheck[k] = {} - Mutators:SendNetworkedMutatorToClients( k, true ) - Mutators:CheckIfClientsHaveMutator( k ) - end - - return true - - end - -end ) - -function Mutators:CheckNetworkMutators( callback_handler ) - if Global.game_settings and not Global.game_settings.single_player then - if GoonBase.Network:IsMultiplayer() and GoonBase.Network:IsHost() and GoonBase.Network:GetNumberOfPeers() > 0 then - return true - end - end - return false -end - -function Mutators:ShowNetworkingMutatorsWindow() - - local title = managers.localization:text("NetworkedMutators_SendingData_Title") - local message = managers.localization:text("NetworkedMutators_SendingData_Message") - local menuOptions = {} - menuOptions[1] = { - text = managers.localization:text("NetworkedMutators_SendingData_Cancel"), - callback = Mutators.NetworkingMutatorsCancel, - is_cancel_button = true - } - local menu = SimpleMenu:New(title, message, menuOptions) - menu.dialog_data.indicator = true - menu:Show() - - self._open_window = menu - -end - -function Mutators:DebugForceStartGame() - MenuCallbackHandler:_start_the_game() -end - -function Mutators:DebugReleaseStartDelay() - MenuCallbackHandler:release_game_start_delay( "MutatorNetworking" ) -end - -function Mutators:NetworkingMutatorsCancel() - MenuCallbackHandler:release_game_start_delay( "MutatorNetworking" ) - MenuCallbackHandler._delayed_start_game = false -end - --- Matchmaking -Hooks:Add("NetworkMatchmakingSetAttributes", "NetworkMatchmakingSetAttributes_" .. Mod:ID(), function(matchmaking, settings) - - local mutator_data = "" - for k, v in pairs( Mutators.ActiveMutators ) do - mutator_data = mutator_data .. k .. "/" - end - if matchmaking and matchmaking._lobby_attributes then - matchmaking._lobby_attributes[ Mutators.MatchmakingData ] = mutator_data - end - -end) - -Hooks:Add("NetworkMatchmakingJoinOKServer", "NetworkMatchmakingJoinOKServer_" .. Mod:ID(), function(matchmaking, room_id, skip_showing_dialog) - - local lobby = Steam:lobby(room_id) - local mutator_key = lobby:key_value( Mutators.MatchmakingData ) - if mutator_key == "value_missing" or mutator_key == "value_pending" then - if Global.game_settings and Global.game_settings.active_mutators then - Global.game_settings.active_mutators = {} - end - return - end - - if Global.game_settings then - Global.game_settings.active_mutators = {} - end - - local mutators_data = string.split( mutator_key, "[/]" ) - for k, v in pairs( mutators_data ) do - if not string.is_nil_or_empty(v) then - Global.game_settings.active_mutators[v] = true - end - end - - Hooks:PreHook( ClientNetworkSession, "on_join_request_cancelled", "NetworkSessionOnJoinRequestCancelled_" .. Mod:ID(), function() - if Global.game_settings then - Global.game_settings.active_mutators = {} - end - end ) - -end) - --- Network Messages -Hooks:Add("NetworkReceivedData", "NetworkReceivedData_" .. Mod:ID(), function(sender, messageType, data) - - if messageType == Mutators.Network.ClearMutators then - Mutators:ClearNetworkedMutators() - end - - if messageType == Mutators.Network.EnableMutator then - Mutators:SetNetworkedMutator( data, true ) - end - - if messageType == Mutators.Network.DisableMutator then - Mutators:SetNetworkedMutator( data, false ) - end - - if messageType == Mutators.Network.MutatorCheck then - Mutators:CheckIfHasMutator( sender, data ) - end - - if messageType == Mutators.Network.MutatorCheckSuccess then - Mutators:MarkClientHasMutator( sender, data, true ) - end - - if messageType == Mutators.Network.MutatorCheckFailure then - Mutators:MarkClientHasMutator( sender, data, false ) - end - -end) - -function Mutators:ClearClientsNetworkedMutators() - GoonBase.Network:SendToPeers( Mutators.Network.ClearMutators, "" ) -end - -function Mutators:ClearNetworkedMutators() - if Global.game_settings then - Global.game_settings.active_mutators = {} - end -end - -function Mutators:SendNetworkedMutatorToClients( mutator_id, enabled ) - if enabled then - GoonBase.Network:SendToPeers( Mutators.Network.EnableMutator, mutator_id ) - else - GoonBase.Network:SendToPeers( Mutators.Network.DisableMutator, mutator_id ) - end -end - -function Mutators:SetNetworkedMutator( mutator_id, enable ) - if Global.game_settings then - if not Global.game_settings.active_mutators then - Global.game_settings.active_mutators = {} - end - Global.game_settings.active_mutators[mutator_id] = enable - end -end - -function Mutators:CheckIfClientsHaveMutator( mutator_id ) - GoonBase.Network:SendToPeers( Mutators.Network.MutatorCheck, mutator_id ) -end - -function Mutators:CheckIfHasMutator( sender, mutator_id ) - - if Mutators.LoadedMutators[ mutator_id ] == nil then - GoonBase.Network:SendToPeer( sender, Mutators.Network.MutatorCheckFailure, mutator_id ) - else - GoonBase.Network:SendToPeer( sender, Mutators.Network.MutatorCheckSuccess, mutator_id ) - end - -end - -function Mutators:MarkClientHasMutator( client, mutator_id, has_mutator ) - - if not Mutators.ClientMutatorCheck then - Mutators.ClientMutatorCheck = {} - end - - if not Mutators.ClientMutatorCheck[ mutator_id ] then - Mutators.ClientMutatorCheck[ mutator_id ] = {} - end - - Mutators.ClientMutatorCheck[ mutator_id ][ client ] = has_mutator - -end - -Hooks:Add("MenuUpdate", "MenuUpdate_" .. Mod:ID(), function(t, dt) - Mutators:CheckMutatorTimeout() -end) - -Hooks:Add("GameSetupUpdate", "GameSetupUpdate_" .. Mod:ID(), function(t, dt) - Mutators:CheckMutatorTimeout() -end) - -function Mutators:CheckMutatorTimeout() - - if self._game_delay_time then - local t = Application:time() - self._game_delay_time - if t > self.NetworkTimeoutTime then - self:CheckAllClientsHaveMutators() - self._game_delay_time = nil - end - end - -end - -function Mutators:CheckAllClientsHaveMutators() - - if not self.ClientMutatorCheck then - return - end - - local missing_mutator = false - local missing_mutator_text = "" - local added_missing_mutator_text = "" - for mutator_id, mutator in pairs( self.ClientMutatorCheck ) do - - if Mutators.LoadedMutators[mutator_id] then - - added_missing_mutator_text = Mutators.LoadedMutators[mutator_id]:GetLocalizedName() - for client_id, has_mutator in pairs( mutator ) do - - if not has_mutator then - - missing_mutator = true - - if not string.is_nil_or_empty( added_missing_mutator_text ) then - missing_mutator_text = missing_mutator_text .. "\n" .. added_missing_mutator_text .. ": " - missing_mutator_text = missing_mutator_text .. GoonBase.Network:GetNameFromPeerID(client_id) - added_missing_mutator_text = "" - else - missing_mutator_text = missing_mutator_text .. ", " .. GoonBase.Network:GetNameFromPeerID(client_id) - end - - end - - end - for k, v in pairs( GoonBase.Network:GetPeers() ) do - local client_id = v:id() - if not mutator[ client_id ] then - - missing_mutator = true - - if not string.is_nil_or_empty( added_missing_mutator_text ) then - missing_mutator_text = missing_mutator_text .. "\n" .. added_missing_mutator_text .. ": " - missing_mutator_text = missing_mutator_text .. GoonBase.Network:GetNameFromPeerID(client_id) - added_missing_mutator_text = "" - else - missing_mutator_text = missing_mutator_text .. ", " .. GoonBase.Network:GetNameFromPeerID(client_id) - end - - end - end - - end - - end - - if missing_mutator then - - managers.system_menu:close( self._open_window.dialog_data.id ) - - local title = managers.localization:text("MissingMutators_Title") - local message = managers.localization:text("MissingMutators_Message") - if not string.is_nil_or_empty( missing_mutator_text ) then - message = message .. missing_mutator_text - end - local menuOptions = {} - menuOptions[1] = { - text = managers.localization:text("MissingMutators_Continue"), - callback = Mutators.ContinueStartGame, - } - menuOptions[2] = { - text = managers.localization:text("MissingMutators_Cancel"), - callback = Mutators.NetworkingMutatorsCancel, - is_cancel_button = true - } - local menu = SimpleMenu:New(title, message, menuOptions) - menu:Show() - - self._open_window = menu - - else - Mutators:ContinueStartGame() - end - -end - -function Mutators.ContinueStartGame() - managers.system_menu:close( Mutators._open_window.dialog_data.id ) - Mutators._open_window = nil - MenuCallbackHandler:release_game_start_delay( "MutatorNetworking" ) - if Mutators:IsInGame() then - MenuCallbackHandler:_start_the_game() - end -end - --- Mission Briefing Screen -Hooks:Add("MissionBriefingGUIPostInit", "MissionBriefingGUIPostInit_" .. Mod:ID(), function(self, saferect_ws, fullrect_ws, node) - - if Mutators:GetNumberOfActiveMutators() > 0 then - self._mutators_item = MutatorsItem:new(self._panel, utf8.to_upper(managers.localization:text("menu_mutators")), Global.game_settings.single_player and 5 or 6) - table.insert(self._items, self._mutators_item) - end - -end) - -MutatorsItem = {} -Hooks:Add("MissionBriefingGUIPreInit", "MissionBriefingGUIPreInit_" .. Mod:ID(), function(self, saferect_ws, fullrect_ws, node) - - MutatorsItem = class( DescriptionItem ) - MutatorsItem.init = function(self, panel, text, i, saved_descriptions) - - MutatorsItem.super.init(self, panel, text, i) - if not managers.job:has_active_job() then - return - end - - local title_text = self._panel:child("title_text") - if title_text then - title_text:set_w( self._panel:w() ) - title_text:set_text( managers.localization:to_upper_text("BriefingMenu_ActiveMutators") ) - end - - local pro_text = self._panel:child("pro_text") - if pro_text then - pro_text:set_text("") - end - - local description_text = self._scroll_panel:child("description_text") - if description_text then - - local mutators_str = "" - for k, v in pairs( Mutators.ActiveMutators ) do - - local mutation = Mutators.LoadedMutators[k] - if mutation then - mutators_str = mutators_str .. mutation:GetLocalizedName() .. ": " .. mutation:GetLocalizedDesc(true) .. "\n" - end - - end - self:set_description_text( mutators_str ) - - end - - end - -end) --- END OF FILE diff --git a/GoonBase/mods/push_to_interact.lua b/GoonBase/mods/push_to_interact.lua deleted file mode 100644 index 9c5772d..0000000 --- a/GoonBase/mods/push_to_interact.lua +++ /dev/null @@ -1,246 +0,0 @@ ----------- --- Payday 2 GoonMod, Public Release Beta 2, built on 1/9/2015 9:30:33 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -local interact_menu_id = "goonbase_pushtointeract_menu" - --- Mod Definition -local Mod = class( BaseMod ) -Mod.id = "PushToInteract" -Mod.Name = "Push to Interact" -Mod.Desc = "Push interact key to toggle interacting with an object" -Mod.Requirements = {} -Mod.Incompatibilities = {} - -Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() - GoonBase.Mods:RegisterMod( Mod ) -end) - -if not Mod:IsEnabled() then - return -end - --- Push to Interact -_G.GoonBase.PushToInteract = _G.GoonBase.PushToInteract or {} -local PushToInteract = _G.GoonBase.PushToInteract -PushToInteract.ForceKeepInteraction = { - ["corpse_alarm_pager"] = true, -} - --- Options -if not GoonBase.Options.PushToInteract then - GoonBase.Options.PushToInteract = {} - GoonBase.Options.PushToInteract.Enabled = true - GoonBase.Options.PushToInteract.GraceTime = 0.2 - GoonBase.Options.PushToInteract.ShowHelper = false -end - --- Localization -local Localization = GoonBase.Localization -Localization.OptionsMenu_PushInteractSubmenuTitle = "Push to Interact" -Localization.OptionsMenu_PushInteractSubmenuDesc = "Change settings for Push to Interact" -Localization.OptionsMenu_PushInteractEnableTitle = "Enable Push to Interact" -Localization.OptionsMenu_PushInteractEnableDesc = "Enable Push to Interact, pushing the interact button will automatically hold the button until it is pushed again" -Localization.OptionsMenu_PushInteractTimeTitle = "Push Grace Period" -Localization.OptionsMenu_PushInteractTimeDesc = "Grace period of the push in seconds. Push-to-interact will only take effect if the button is held for this long" -Localization.OptionsMenu_PushInteractHelperTitle = "Enable Helper Indicator" -Localization.OptionsMenu_PushInteractHelperDesc = "Show a blue outline around the interaction timer when Push-to-interact will hold the interaction for you" -Localization.OptionsMenu_PushInteractTimeDesc_Default = Localization.OptionsMenu_PushInteractTimeDesc - --- Functions -function PushToInteract:CreateInteractionIndicator() - - if not self._workspace and GoonBase.Options.PushToInteract.ShowHelper then - - self._workspace = Overlay:newgui():create_screen_workspace(0, 0, 1, 1) - self._interaction_radius = self._workspace:panel():w() / 12 - self._interaction_circle = CircleBitmapGuiObject:new(self._workspace:panel(), { - use_bg = false, - radius = self._interaction_radius, - sides = 64, - current = 64, - total = 64, - color = Color.white:with_alpha(1), - blend_mode = "add", - image = "guis/textures/pd2/specialization/progress_ring", - layer = 2, - x = self._workspace:panel():w() / 2 - self._interaction_radius - 0.5, - y = self._workspace:panel():h() / 2 - self._interaction_radius - 0.5, - }) - self._interaction_circle:set_current( 0 ) - - end - -end - -function PushToInteract:DestroyWorkspace() - - if self._workspace and alive(self._workspace) then - Overlay:newgui():destroy_workspace(self._workspace) - self._workspace = nil - end - -end - -function PushToInteract:ShowInteractionHelper() - if not GoonBase.Options.PushToInteract.ShowHelper then - return - end - if not self._interaction_circle then - PushToInteract:CreateInteractionIndicator() - end - self._interaction_circle:set_current( 1 ) -end - -function PushToInteract:UpdateInteractionHelper(t) - if not GoonBase.Options.PushToInteract.ShowHelper then - return - end - if not self._interaction_circle then - PushToInteract:CreateInteractionIndicator() - end - self._interaction_circle:set_current( t ) -end - -function PushToInteract:HideInteractionHelper() - if not GoonBase.Options.PushToInteract.ShowHelper then - return - end - if not self._interaction_circle then - PushToInteract:CreateInteractionIndicator() - end - self._interaction_circle:set_current( 0 ) -end - --- Hooks -Hooks:Add("PlayerStandardCheckActionInteract", "PlayerStandardCheckActionInteract_PushToInteract", function(ply, t, input) - - if not GoonBase.Options.PushToInteract.Enabled then - return - end - - local grace_time = (GoonBase.Options.PushToInteract.GraceTime or 0.2) - ply._last_interact_press_t = ply._last_interact_press_t or 0 - - if input.btn_interact_press then - - ply._last_interact_press_t = t - - if ply:_interacting() then - ply:_interupt_action_interact() - PushToInteract:HideInteractionHelper() - return false - end - - elseif input.btn_interact_release then - - local dt = t - ply._last_interact_press_t - local always_use = grace_time < 0.001 - - if managers.interaction and alive( managers.interaction:active_object() ) then - local tw = managers.interaction:active_object():interaction().tweak_data - if PushToInteract.ForceKeepInteraction[tw] then - always_use = true - end - end - - if always_use or dt >= grace_time then - return false - end - - end - - if ply._last_interact_press_t and ply:_interacting() then - local dt = t - ply._last_interact_press_t - if dt >= grace_time then - - if ply._interact_expire_t then - local x = (t - ply._last_interact_press_t) / (ply._interact_expire_t - ply._last_interact_press_t) - PushToInteract:UpdateInteractionHelper( x ) - else - PushToInteract:ShowInteractionHelper() - end - - else - PushToInteract:HideInteractionHelper() - end - else - PushToInteract:HideInteractionHelper() - end - -end) - --- Menu -Hooks:Add("MenuManagerSetupCustomMenus", "MenuManagerSetupCustomMenus_PushToInteract", function( menu_manager, menu_nodes ) - GoonBase.MenuHelper:NewMenu( interact_menu_id ) -end) - -Hooks:Add("MenuManagerSetupGoonBaseMenu", "MenuManagerSetupGoonBaseMenu_PushToInteract", function( menu_manager ) - - -- Add corpse mod menu button - GoonBase.MenuHelper:AddButton({ - id = "pushtointeract_mod_menu_button", - title = "OptionsMenu_PushInteractSubmenuTitle", - desc = "OptionsMenu_PushInteractSubmenuDesc", - next_node = interact_menu_id, - menu_id = "goonbase_options_menu" - }) - - -- Callbacks - MenuCallbackHandler.toggle_pushtointeract = function(this, item) - GoonBase.Options.PushToInteract.Enabled = item:value() == "on" and true or false - GoonBase.Options:Save() - end - - MenuCallbackHandler.set_pushtointeract_grace_period = function(this, item) - GoonBase.Options.PushToInteract.GraceTime = tonumber( item:value() ) - GoonBase.Options:Save() - end - - MenuCallbackHandler.toggle_pushtointeract_helper = function(this, item) - GoonBase.Options.PushToInteract.ShowHelper = item:value() == "on" and true or false - GoonBase.Options:Save() - end - - -- Menu - GoonBase.MenuHelper:AddToggle({ - id = "pushtointeract_toggle", - title = "OptionsMenu_PushInteractEnableTitle", - desc = "OptionsMenu_PushInteractEnableDesc", - callback = "toggle_pushtointeract", - value = GoonBase.Options.PushToInteract.Enabled, - menu_id = interact_menu_id, - priority = 50 - }) - - GoonBase.MenuHelper:AddSlider({ - id = "pushtointeract_timer_slider", - title = "OptionsMenu_PushInteractTimeTitle", - desc = "OptionsMenu_PushInteractTimeDesc", - callback = "set_pushtointeract_grace_period", - value = GoonBase.Options.PushToInteract.GraceTime, - min = 0, - max = 2, - step = 0.01, - show_value = true, - menu_id = interact_menu_id, - priority = 49 - }) - - GoonBase.MenuHelper:AddToggle({ - id = "pushtointeract_toggle_showhelper", - title = "OptionsMenu_PushInteractHelperTitle", - desc = "OptionsMenu_PushInteractHelperDesc", - callback = "toggle_pushtointeract_helper", - value = GoonBase.Options.PushToInteract.ShowHelper or false, - menu_id = interact_menu_id, - priority = 48 - }) - -end) - -Hooks:Add("MenuManagerBuildCustomMenus", "MenuManagerBuildCustomMenus_PushToInteract", function( menu_manager, mainmenu_nodes ) - mainmenu_nodes[interact_menu_id] = GoonBase.MenuHelper:BuildMenu( interact_menu_id ) -end) --- END OF FILE diff --git a/GoonBase/mods/trading.lua b/GoonBase/mods/trading.lua deleted file mode 100644 index eda4c44..0000000 --- a/GoonBase/mods/trading.lua +++ /dev/null @@ -1,1579 +0,0 @@ ----------- --- Payday 2 GoonMod, Public Release Beta 2, built on 1/10/2015 2:53:10 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - --- Mod Definition -local Mod = class( BaseMod ) -Mod.id = "Trading" -Mod.Name = "Crime.net Cargo" -Mod.Desc = "Send and receive weapons, weapon mods, masks, and mask parts between your friends and other players" -Mod.Requirements = {} -Mod.Incompatibilities = {} - -Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() - GoonBase.Mods:RegisterMod( Mod ) -end) - -if not Mod:IsEnabled() then - return -end - --- Trading -_G.GoonBase.Trading = _G.GoonBase.Trading or {} -local Trading = _G.GoonBase.Trading - -Trading.TradablePeers = Trading.TradablePeers or {} -Trading.BlackMarketGUI = nil -Trading.TradeData = {} -Trading.ActiveTradeWindow = nil -Trading.MenuId = "goonbase_trading_options_menu" - -Trading.BlacklistedMasks = { - ["character_locked"] = true, -} - --- Categories -Trading.Categories = {} -Trading.Categories.PrimaryWeapon = "primaries" -Trading.Categories.SecondaryWeapon = "secondaries" -Trading.Categories.WeaponMod = "weapon_mod" -Trading.Categories.Mask = "masks" -Trading.Categories.MaskColour = "colors" -Trading.Categories.MaskPattern = "textures" -Trading.Categories.MaskMaterial = "materials" - --- Network -Trading.Network = Trading.Network or {} -Trading.Network.MessageType = Trading.Network.MessageType or {} -Trading.Network.MessageType.System = "TradeSystem" -Trading.Network.MessageType.Request = "TradeRequest" -Trading.Network.MessageType.RequestResponse = "TradeRequestResponse" -Trading.Network.MessageType.Cancel = "TradeCancel" -Trading.Network.MessageType.Category = "TradeCategory" -Trading.Network.MessageType.Weapon = "TradeWeapon" -Trading.Network.MessageType.WeaponMods = "TradeWeaponMods" -Trading.Network.MessageType.SingleWeaponMod = "TradeSingleWeaponMod" -Trading.Network.MessageType.Mask = "TradeMask" -Trading.Network.MessageType.MaskColour = "TradeColour" -Trading.Network.MessageType.MaskPattern = "TradePattern" -Trading.Network.MessageType.MaskMaterial = "TradeMaterial" - -Trading.Network.RequestTradability = "RequestTradability" -Trading.Network.TradabilityHandshake = "TradabilityHandshake" - -Trading.Network.TradeAccept = "Accept" -Trading.Network.TradeDecline = "Decline" -Trading.Network.AutoDecline_PrimaryWeaponSlots = "DeclinePrimarySlots" -Trading.Network.AutoDecline_SecondaryWeaponSlots = "DeclineSecondarySlots" -Trading.Network.AutoDecline_MaskSlots = "DeclineMaskSlots" -Trading.Network.AutoDecline_AlreadyTrading = "AlreadyTrading" - --- Options -if not GoonBase.Options.Trading then - GoonBase.Options.Trading = {} - GoonBase.Options.Trading.Enabled = true - GoonBase.Options.Trading.FixedPreferredCharacterMask = false -end - --- Localization -local Localization = GoonBase.Localization -Localization.Trading_OptionsMenuTitle = "Crime.net Cargo" -Localization.Trading_OptionsMenuMessage = "Modify Crime.net Cargo Settings" -Localization.Trading_OptionsTitle = "Enable Crime.net Cargo" -Localization.Trading_OptionsMessage = "Enable Crime.net Cargo Modification" - -Localization.Trading_InventorySendToPlayer = "Crime.net Cargo" -Localization.Trading_TradeWindowTitle = "Crime.net Cargo" -Localization.Trading_TradeWindowMessage = "Send {1} to:" -Localization.Trading_TradeWindowCancel = "Cancel" -Localization.Trading_TradeWindowOk = "OK" - -Localization.Trading_OnlineOnlyTitle = "Crime.net Cargo Offline" -Localization.Trading_OnlineOnlyMessage = "You must be in a lobby to send cargo to somebody!"; -Localization.Trading_OnlineOnlyCancel = "OK" - -Localization.Trading_NoPeersTitle = "Crime.net Cargo" -Localization.Trading_NoPeersMessage = "There are no players who can receive cargo in the lobby!" -Localization.Trading_NoPeersCancel = "OK" - -Localization.Trading_TradeRequestMessage = "{1} would like to send you {2} ({3})." -Localization.Trading_TradeRequestAccept = "Accept" -Localization.Trading_TradeRequestDecline = "Decline" -Localization.Trading_TradeAccepted = "You have sent {1} to {2}!" -Localization.Trading_TradeDeclined = "{1} declined your {2}." - -Localization.Trading_InventoryFull = "{1} is trying to send you {2}, but you do not have any free {3} slots." -Localization.Trading_InventoryFullReason = "{1} could not be sent to {2}, as they do not have enough free {3} slots." - -Localization.Trading_WeaponModRemovedDLC = "The {1} attached to this weapon has been removed as you do not own the '{2}' downloadable content." -Localization.Trading_WeaponSendWithMods = "Do you want to send the mods attached to this weapon too?" -Localization.Trading_WeaponSendMods = "Send with mods" -Localization.Trading_WeaponDontSendMods = "Don't send mods" - -Localization.Trading_WeaponModReceived = "You have received {1} from {2}." -Localization.Trading_WeaponModReceivedNoDLC = "You have received {1} from {2}, but you do not own the '{3}' downloadable content and may not be able to use it." -Localization.Trading_WeaponModAccept = "OK" - -Localization.Trading_MaskReceived = "You have received {1} from {2}." -Localization.Trading_MaskReceivedNoDLC = "You have received {1} from {2}, but you do not own the {3} downloadable content/s. Your items have been added to your stash." -Localization.Trading_MaskReceivedAccept = "OK" - -Localization.Trading_UntradableMask = "The mask '{1}' is untradable. {2} must be returned to the stash and can not be traded to other players." -Localization.Trading_UntradableMaskAccept = "OK" - -Localization.Trading_UntradableMaskMod = "The mask mod '{1}' is untradable, as it must be returned to the stash and can not be traded to other players." -Localization.Trading_UntradableMaskModAccept = "OK" - -Localization.Trading_WaitingResponseTitle = "Awaiting Response" -Localization.Trading_WaitingResponseMessage = "Awaiting response from {1}." -Localization.Trading_WaitingResponseCancel = "Cancel Trade" - -Localization.Trading_OtherCancelledTitle = "Trade Cancelled" -Localization.Trading_OtherCancelledMessage = "{1} has cancelled the trade with you." -Localization.Trading_OtherCancelledAccept = "OK" - -Localization.Trading_AlreadyTradingTitle = "Trade Unavailable" -Localization.Trading_AlreadyTradingMessage = "{1} is unavailable to trade with at the moment, as they are already in another trade." -Localization.Trading_AlreadyTradingAccept = "OK" - --- DLC Names -Trading.DLCNames = { - infamous = "Infamy", - twitch_pack = "Twitch Pack", - dlc1 = "Armoured Transport", - armored_transport = "Armoured Transport", - gage_pack = "Gage Weapon Pack #01", - gage_pack_lmg = "Gage Weapon Pack #02", - gage_pack_jobs = "Gage Mod Courier", - gage_pack_snp = "Gage Sniper Pack", - gage_pack_assault = "Gage Assault Pack", - big_bank = "The Big Bank Heist", - gage_pack_shotgun = "Gage Shotgun Pack", - season_pass = "Season Pass", - animal = "Animal" -} - --- Options Menu -Hooks:Add("MenuManagerSetupCustomMenus", "MenuManagerSetupCustomMenus_Trading", function(menu_manager, menu_nodes) - GoonBase.MenuHelper:NewMenu( Trading.MenuId ) -end) - -Hooks:Add("MenuManagerSetupGoonBaseMenu", "MenuManagerSetupGoonBaseMenu_Trading", function( menu_manager ) - - GoonBase.MenuHelper:AddButton({ - id = "trading_submenu_options_button", - title = "Trading_OptionsMenuTitle", - desc = "Trading_OptionsMenuMessage", - next_node = Trading.MenuId, - menu_id = "goonbase_options_menu", - }) - - MenuCallbackHandler.toggle_trading_enabled = function(this, item) - GoonBase.Options.Trading.Enabled = item:value() == "on" and true or false - GoonBase.Options:Save() - end - - GoonBase.MenuHelper:AddToggle({ - id = "toggle_trading_enabled", - title = "Trading_OptionsTitle", - desc = "Trading_OptionsMessage", - callback = "toggle_trading_enabled", - value = GoonBase.Options.Trading.Enabled, - menu_id = Trading.MenuId, - }) - -end) - -Hooks:Add("MenuManagerBuildCustomMenus", "MenuManagerBuildCustomMenus_Trading", function(menu_manager, mainmenu_nodes) - mainmenu_nodes[Trading.MenuId] = GoonBase.MenuHelper:BuildMenu( Trading.MenuId ) -end) - --- Blackmarket Menu -Hooks:Add("BlackMarketGUIPostSetup", "BlackMarketGUIPostSetup_InjectTradeButton", function(gui, is_start_page, component_data) - - Hooks:RegisterHook("TradingAttemptTradeWeapon") - gui.trade_weapon_callback = function(self, data) - Hooks:Call("TradingAttemptTradeWeapon", data) - end - - Hooks:RegisterHook("TradingAttemptTradeWeaponMod") - gui.trade_weaponmod_callback = function(self, data) - Hooks:Call("TradingAttemptTradeWeaponMod", data) - end - - Hooks:RegisterHook("TradingAttemptTradeMask") - gui.trade_mask_callback = function(self, data) - Hooks:Call("TradingAttemptTradeMask", data) - end - - Hooks:RegisterHook("TradingAttemptTradeMaskPart") - gui.trade_mask_part_callback = function(self, data) - Hooks:Call("TradingAttemptTradeMaskPart", data) - end - - local w_trade = { - prio = 5, - btn = "BTN_STICK_L", - pc_btn = Idstring("menu_toggle_ready"), - name = "Trading_InventorySendToPlayer", - callback = callback(gui, gui, "trade_weapon_callback") - } - - local m_trade = { - prio = 5, - btn = "BTN_STICK_L", - pc_btn = Idstring("menu_toggle_ready"), - name = "Trading_InventorySendToPlayer", - callback = callback(gui, gui, "trade_mask_callback") - } - - local wm_trade = { - prio = 5, - btn = "BTN_STICK_L", - pc_btn = Idstring("menu_toggle_ready"), - name = "Trading_InventorySendToPlayer", - callback = callback(gui, gui, "trade_weaponmod_callback") - } - - local mp_trade = { - prio = 5, - btn = "BTN_STICK_L", - pc_btn = Idstring("menu_toggle_ready"), - name = "Trading_InventorySendToPlayer", - callback = callback(gui, gui, "trade_mask_part_callback") - } - - local btn_x = 10 - gui._btns["w_trade"] = BlackMarketGuiButtonItem:new(gui._buttons, w_trade, btn_x) - gui._btns["m_trade"] = BlackMarketGuiButtonItem:new(gui._buttons, m_trade, btn_x) - gui._btns["wm_trade"] = BlackMarketGuiButtonItem:new(gui._buttons, wm_trade, btn_x) - gui._btns["mp_trade"] = BlackMarketGuiButtonItem:new(gui._buttons, mp_trade, btn_x) - -end) - -Hooks:Add("MenuUpdate", "MenuUpdate_" .. Mod:ID(), function(t, dt) - - -- This is a mess, but it gets custom keybinds for menu items working - if not managers.menu:is_pc_controller() and managers.menu:get_controller():get_input_pressed("run") then - - local psuccess, perror = pcall(function() - - if not managers.menu_component then return end - - local blackmarket_gui = managers.menu_component._blackmarket_gui - if not blackmarket_gui then return end - if not blackmarket_gui._selected_slot then return end - if not blackmarket_gui._selected_slot._data then return end - - local data = blackmarket_gui._selected_slot._data - if data == nil then return end - local category = data.category - if category == Trading.Categories.PrimaryWeapon or category == Trading.Categories.SecondaryWeapon then - blackmarket_gui:trade_weapon_callback( data ) - end - if category == Trading.Categories.WeaponMod then - blackmarket_gui:trade_weaponmod_callback( data ) - end - if category == Trading.Categories.Mask then - blackmarket_gui:trade_mask_callback( data ) - end - if category == Trading.Categories.MaskColour or category == Trading.Categories.MaskPattern or category == Trading.Categories.MaskMaterial then - blackmarket_gui:trade_mask_part_callback( data ) - end - - end) - if not psuccess then - Print("[Error] " .. perror) - end - - end - -end) - -Hooks:Add("BlackMarketGUIOnPopulateWeaponActionList", "BlackMarketGUIOnPopulateWeaponActionList_" .. Mod:ID(), function(gui, data) - - if GoonBase.Options.Trading.Enabled and not data.last_weapon and not data.equipped and GoonBase.Network:IsMultiplayer() then - table.insert(data, "w_trade") - end - -end) - -Hooks:Add("BlackMarketGUIOnPopulateMasksActionList", "BlackMarketGUIOnPopulateMasksActionList_" .. Mod:ID(), function(gui, data) - - Trading:_FixPreferredCharacterMask() - - if GoonBase.Options.Trading.Enabled and not data.equipped and GoonBase.Network:IsMultiplayer() then - if not Trading.BlacklistedMasks[ data.name ] then - table.insert(data, "m_trade") - end - end - -end) - -Hooks:Add("BlackMarketGUIOnPopulateModsActionList", "BlackMarketGUIOnPopulateModsActionList_" .. Mod:ID(), function(gui, data) - - if GoonBase.Options.Trading.Enabled and GoonBase.Network:IsMultiplayer() then - if type(data.unlocked) ~= "number" or data.unlocked > 0 then - table.insert(data, "wm_trade") - end - end - -end) - -Hooks:Add("BlackMarketGUIOnPopulateMaskModsActionList", "BlackMarketGUIOnPopulateMaskModsActionList_" .. Mod:ID(), function(gui, data) - - if GoonBase.Options.Trading.Enabled and not data.equipped and data.amount ~= nil and data.amount > 0 and GoonBase.Network:IsMultiplayer() then - table.insert(data, "mp_trade") - end - -end) - --- Request client tradability when we open the inventory -Hooks:Add("BlackMarketGUIOnPopulateWeapons", "BlackMarketGUIOnPopulateWeapons_Trading", function(gui, category, data) - - if not GoonBase.Options.Trading.Enabled then - return - end - - Trading.BlackMarketGUI = gui - - -- Request tradability from peers - if GoonBase.Network:IsMultiplayer() then - Trading.TradablePeers = {} - GoonBase.Network:SendToPeers(Trading.Network.MessageType.System, Trading.Network.RequestTradability) - end - -end) - -Hooks:Add("BlackMarketGUIOnPopulateMasks", "BlackMarketGUIOnPopulateMasks_Trading", function(gui, category, data) - - if not GoonBase.Options.Trading.Enabled then - return - end - - Trading.BlackMarketGUI = gui - -end) - -Hooks:Add("BlackMarketGUIOnPopulateMods", "BlackMarketGUIOnPopulateMods_Trading", function(gui, category, data) - - if not GoonBase.Options.Trading.Enabled then - return - end - - Trading.BlackMarketGUI = gui - -end) - -Hooks:Add("BlackMarketGUIOnPopulateMaskMods", "BlackMarketGUIOnPopulateMaskMods_Trading", function(gui, category, data) - - if not GoonBase.Options.Trading.Enabled then - return - end - - Trading.BlackMarketGUI = gui - -end) - --- Check if we have any tradable peers -function Trading:HasTradablePeers() - - local tradable_peers = 0 - for k, v in pairs( managers.network:session():peers() ) do - if Trading.TradablePeers[k] == true then - tradable_peers = tradable_peers + 1 - end - end - - if tradable_peers < 1 then - return false - end - - return true - -end - --- Process trade messages -Hooks:Add("NetworkReceivedData", "NetworkReceivedData_Trading", function(sender, messageType, data) - - -- Don't process if disabled - if not GoonBase.Options.Trading.Enabled then - return - end - - -- Trade System Messages - if messageType == Trading.Network.MessageType.System then - - if data == Trading.Network.RequestTradability then - Trading:RequestTradabilityHandshake(sender) - return - end - if data == Trading.Network.TradabilityHandshake then - Trading:CompleteTradabilityHandshake(sender) - return - end - - end - - -- Trade Response - if messageType == Trading.Network.MessageType.RequestResponse then - Trading:TradeResponseReceived(sender, data) - return - end - - -- Trade Already-In-Progress Reply - if Trading.TradeData.CurrentlyTradingWith ~= nil and Trading.TradeData.CurrentlyTradingWith ~= sender then - GoonBase.Network:SendToPeer(sender, Trading.Network.MessageType.RequestResponse, Trading.Network.AutoDecline_AlreadyTrading) - return - end - - -- Make sure player only ever gets trade data from one player - Trading.TradeData.CurrentlyTradingWith = sender - - -- Trade Requests - if messageType == Trading.Network.MessageType.Request then - Trading:TradeRequested(sender) - return - end - - -- Trade Cancel - if messageType == Trading.Network.MessageType.Cancel then - Trading:HandleTradeCancelled(sender) - return - end - - -- Trade Data - if messageType == Trading.Network.MessageType.Category then - Trading.TradeData.Category = data - return - end - - if messageType == Trading.Network.MessageType.Weapon then - Trading.TradeData.Weapon = data - return - end - - if messageType == Trading.Network.MessageType.WeaponMods then - if Trading.TradeData.WeaponMods == nil then - Trading.TradeData.WeaponMods = {} - end - table.insert( Trading.TradeData.WeaponMods, data ) - return - end - - if messageType == Trading.Network.MessageType.SingleWeaponMod then - Trading.TradeData.WeaponMod = data - return - end - - -- Mask Data - if messageType == Trading.Network.MessageType.Mask then - Trading.TradeData.Mask = data - end - - -- Mask Mod Data - if messageType == Trading.Network.MessageType.MaskPattern then - if Trading.TradeData.MaskMods == nil then - Trading.TradeData.MaskMods = {} - end - Trading.TradeData.MaskMods.Pattern = data - return - end - - if messageType == Trading.Network.MessageType.MaskMaterial then - if Trading.TradeData.MaskMods == nil then - Trading.TradeData.MaskMods = {} - end - Trading.TradeData.MaskMods.Material = data - return - end - - if messageType == Trading.Network.MessageType.MaskColour then - if Trading.TradeData.MaskMods == nil then - Trading.TradeData.MaskMods = {} - end - Trading.TradeData.MaskMods.Colour = data - return - end - -end) - --- Handle tradability handshake -function Trading:RequestTradabilityHandshake(sender) - if GoonBase.Options.Trading.Enabled then - GoonBase.Network:SendToPeer(sender, Trading.Network.MessageType.System, Trading.Network.TradabilityHandshake) - end -end -function Trading:CompleteTradabilityHandshake(sender) - Trading.TradablePeers[sender] = true -end - --- Handle trade request -function Trading:TradeRequested(sender, data) - - Trading.TradeData.Trader = sender - local category = Trading.TradeData.Category - - -- Check if player has enough primary weapon slots to trade - if category == Trading.Categories.PrimaryWeapon then - - local slot = managers.blackmarket:_get_free_weapon_slot( Trading.Categories.PrimaryWeapon ) - local weaponName = managers.weapon_factory:get_weapon_name_by_weapon_id( Trading.TradeData.Weapon ) - if slot ~= nil then - - Trading:ShowTradeResponseWindow(sender, { name = weaponName, category = "Primary Weapon" }) - else - Trading:ShowFullInventoryWindow(sender, { item = weaponName, slot = "primary weapon" }) - GoonBase.Network:SendToPeer(sender, Trading.Network.MessageType.RequestResponse, Trading.Network.AutoDecline_PrimaryWeaponSlots) - end - - end - - -- Check if player has enough secondary weapon slots to trade - if category == Trading.Categories.SecondaryWeapon then - - local slot = managers.blackmarket:_get_free_weapon_slot( Trading.Categories.SecondaryWeapon ) - local weaponName = managers.weapon_factory:get_weapon_name_by_weapon_id( Trading.TradeData.Weapon ) - if slot ~= nil then - Trading:ShowTradeResponseWindow(sender, { name = weaponName, category = "Secondary Weapon" }) - else - Trading:ShowFullInventoryWindow(sender, { item = weaponName, slot = "secondary weapon" }) - GoonBase.Network:SendToPeer(sender, Trading.Network.MessageType.RequestResponse, Trading.Network.AutoDecline_SecondaryWeaponSlots) - end - - end - - -- Single Weapon Mod Trade - if category == Trading.Categories.WeaponMod then - local modName = managers.weapon_factory:get_part_name_by_part_id( Trading.TradeData.WeaponMod ) - Trading:ShowTradeResponseWindow(sender, { name = modName, category = "Weapon Mod" }) - end - - -- Mask Trading - local success, err = pcall(function() - - if category == Trading.Categories.Mask then - - local mask_name = managers.blackmarket:get_mask_name( Trading.TradeData.Mask ) - - -- Check if player is trying to send premodded mask - if Trading.TradeData.MaskMods ~= nil then - - -- Check if player has free mask slots - local slot = managers.blackmarket:get_free_mask_slot() - if slot ~= nil then - Trading:ShowTradeResponseWindow(sender, { name = mask_name or "error: mask_name", category = "Mask" }) - else - Trading:ShowFullInventoryWindow(sender, { item = mask_name or "error: mask_name", slot = "mask" }) - GoonBase.Network:SendToPeer(sender, Trading.Network.MessageType.RequestResponse, Trading.Network.AutoDecline_MaskSlots) - end - - else - Trading:ShowTradeResponseWindow(sender, { name = mask_name or "error: mask_name", category = "Mask" }) - end - - end - - -- Mask Material - if category == Trading.Categories.MaskMaterial then - local mod_name = managers.blackmarket:get_mask_mod_name( Trading.TradeData.MaskMods.Material, Trading.Categories.MaskMaterial ) - Trading:ShowTradeResponseWindow(sender, { name = mod_name or "error: mod_name", category = "Mask Material" }) - end - - -- Mask Pattern - if category == Trading.Categories.MaskPattern then - local mod_name = managers.blackmarket:get_mask_mod_name( Trading.TradeData.MaskMods.Pattern, Trading.Categories.MaskPattern ) - Trading:ShowTradeResponseWindow(sender, { name = mod_name or "error: mod_name", category = "Mask Pattern" }) - end - - -- Mask Color - if category == Trading.Categories.MaskColour then - local mod_name = managers.blackmarket:get_mask_mod_name( Trading.TradeData.MaskMods.Colour, Trading.Categories.MaskColour ) - Trading:ShowTradeResponseWindow(sender, { name = mod_name or "error: mod_name", category = "Mask Colours" }) - end - - end) - if not success then Print(err) end - -end - -function Trading:ShowTradeResponseWindow(sender, data) - - local senderName = GoonBase.Network:GetNameFromPeerID(sender) - - local title = managers.localization:text("Trading_TradeWindowTitle") - local message = managers.localization:text("Trading_TradeRequestMessage") - message = message:gsub("{1}", senderName) - message = message:gsub("{2}", data.name) - message = message:gsub("{3}", data.category) - local menuOptions = {} - menuOptions[1] = { - text = managers.localization:text("Trading_TradeRequestAccept"), - callback = Trading.TradeRequestAccept, - is_cancel_button = true - } - menuOptions[2] = { - text = managers.localization:text("Trading_TradeRequestDecline"), - callback = Trading.TradeRequestDecline, - is_cancel_button = true - } - Trading.ActiveTradeWindow = SimpleMenu:New(title, message, menuOptions) - Trading.ActiveTradeWindow:Show() - -end - -function Trading:ShowFullInventoryWindow(sender, data) - - local senderName = GoonBase.Network:GetNameFromPeerID(sender) - - local title = managers.localization:text("Trading_TradeWindowTitle") - local message = managers.localization:text("Trading_InventoryFull") - message = message:gsub("{1}", senderName) - message = message:gsub("{2}", data.item) - message = message:gsub("{3}", data.slot) - local menuOptions = {} - menuOptions[1] = { - text = managers.localization:text("Trading_TradeWindowOk"), - is_cancel_button = true - } - local tradeMenu = SimpleMenu:New(title, message, menuOptions) - tradeMenu:Show() - -end - -function Trading.TradeRequestAccept(data) - - GoonBase.Network:SendToPeer(Trading.TradeData.Trader, Trading.Network.MessageType.RequestResponse, Trading.Network.TradeAccept) - - local category = Trading.TradeData.Category - - if category == Trading.Categories.PrimaryWeapon then - Trading:AddWeaponToInventory( Trading.TradeData.Weapon, Trading.TradeData.WeaponMods ) - managers.menu:back(true) - end - - if category == Trading.Categories.SecondaryWeapon then - Trading:AddWeaponToInventory( Trading.TradeData.Weapon, Trading.TradeData.WeaponMods ) - managers.menu:back(true) - end - - if category == Trading.Categories.WeaponMod then - Trading:AddTradedModToInventory() - managers.menu:back(true) - managers.menu:back(true) - end - - if category == Trading.Categories.Mask then - Trading:AddTradedMaskToInventory() - managers.menu:back(true) - end - - if category == Trading.Categories.MaskMaterial or category == Trading.Categories.MaskPattern or category == Trading.Categories.MaskColour then - Trading:AddTradedMaskModToInventory() - end - - Trading.TradeData = {} - -end - -function Trading.TradeRequestDecline(data) - GoonBase.Network:SendToPeer(Trading.TradeData.Trader, Trading.Network.MessageType.RequestResponse, Trading.Network.TradeDecline) - Trading.TradeData = {} -end - --- Handle Trade Response -function Trading:TradeResponseReceived(sender, data) - - if data == Trading.Network.TradeAccept then - Trading:TradeResponseAccept(sender) - return - end - - if data == Trading.Network.AutoDecline_PrimaryWeaponSlots then - Trading:TradeResponseFullInventory("primary weapon") - return - end - - if data == Trading.Network.AutoDecline_SecondaryWeaponSlots then - Trading:TradeResponseFullInventory("secondary weapon") - return - end - - if data == Trading.Network.AutoDecline_MaskSlots then - Trading:TradeResponseFullInventory("mask") - return - end - - if data == Trading.Network.AutoDecline_AlreadyTrading then - Trading:TradeResponseAlreadyTrading() - return - end - - Trading:TradeResponseDecline(sender) - -end - -function Trading:TradeResponseAccept(sender) - - local success, err = pcall(function() - - local category = Trading.TradeData.Category - - if category == Trading.Categories.PrimaryWeapon or category == Trading.Categories.SecondaryWeapon then - Trading:ShowTradeResponseAcceptWindow( sender, Trading.TradeData.RawData.slot.name_localized ) - Trading:RemoveWeaponFromInventory() - managers.menu:back(true) - end - - if category == Trading.Categories.WeaponMod then - Trading:ShowTradeResponseAcceptWindow( sender, Trading.TradeData.RawData.slot.name_localized ) - Trading:RemoveModFromInventory() - managers.menu:back(true) - managers.menu:back(true) - end - - if category == Trading.Categories.Mask then - Trading:ShowTradeResponseAcceptWindow( sender, managers.blackmarket:get_mask_name( Trading.TradeData.RawData.slot.mask_id ) ) - managers.blackmarket:remove_mask_from_inventory( Trading.TradeData.RawData.slot ) - managers.menu:back(true) - end - - if category == Trading.Categories.MaskMaterial or category == Trading.Categories.MaskPattern or category == Trading.Categories.MaskColour then - Trading:ShowTradeResponseAcceptWindow( sender, managers.blackmarket:get_mask_mod_name( Trading.TradeData.RawData.slot.name, Trading.TradeData.RawData.slot.category ) ) - -- TODO - managers.blackmarket:remove_mask_mod_from_inventory( Trading.TradeData.RawData.slot.name, Trading.TradeData.RawData.slot.category ) - end - - end) - - if not success then Print(err) end - -end - -function Trading:ShowTradeResponseAcceptWindow(sender, item_name) - - Trading:CloseActiveWindow() - - local title = managers.localization:text("Trading_TradeWindowTitle") - local message = managers.localization:text("Trading_TradeAccepted") - message = message:gsub("{1}", item_name) - message = message:gsub("{2}", GoonBase.Network:GetNameFromPeerID(Trading.TradeData.Recipient)) - local menuOptions = {} - menuOptions[1] = { text = managers.localization:text("Trading_TradeWindowOk"), is_cancel_button = true } - local tradeMenu = SimpleMenu:New(title, message, menuOptions) - tradeMenu:Show() - -end - -function Trading:TradeResponseDecline(sender) - - local success, err = pcall(function() - - Trading:CloseActiveWindow() - - if Trading.TradeData.Recipient == nil then - return - end - - local category = Trading.TradeData.Category - local item_name = "trade" - if category == Trading.Categories.PrimaryWeapon or category == Trading.Categories.SecondaryWeapon or category == Trading.Categories.WeaponMod then - item_name = Trading.TradeData.RawData.slot.name_localized or item_name - end - if category == Trading.Categories.Mask then - item_name = managers.blackmarket:get_mask_name( Trading.TradeData.RawData.slot.mask_id ) - end - - local title = managers.localization:text("Trading_TradeWindowTitle") - local message = managers.localization:text("Trading_TradeDeclined") - message = message:gsub("{1}", GoonBase.Network:GetNameFromPeerID(Trading.TradeData.Recipient)) - message = message:gsub("{2}", item_name) - local menuOptions = {} - menuOptions[1] = { text = managers.localization:text("Trading_TradeWindowOk"), is_cancel_button = true } - local tradeMenu = SimpleMenu:New(title, message, menuOptions) - tradeMenu:Show() - - Trading.TradeData = {} - - end) - if not success then Print(err) end - -end - -function Trading:TradeResponseAlreadyTrading() - - local success, err = pcall(function() - - Trading:CloseActiveWindow() - - if Trading.TradeData.Recipient == nil then - return - end - - local title = managers.localization:text("Trading_AlreadyTradingTitle") - local message = managers.localization:text("Trading_AlreadyTradingMessage") - message = message:gsub("{1}", GoonBase.Network:GetNameFromPeerID(Trading.TradeData.Recipient)) - local menuOptions = {} - menuOptions[1] = { text = managers.localization:text("Trading_AlreadyTradingAccept"), is_cancel_button = true } - local tradeMenu = SimpleMenu:New(title, message, menuOptions) - tradeMenu:Show() - - Trading.TradeData = {} - - end) - if not success then Print(err) end - -end - -function Trading:TradeResponseFullInventory(reason) - - Trading:CloseActiveWindow() - - local title = managers.localization:text("Trading_TradeWindowTitle") - local message = managers.localization:text("Trading_InventoryFullReason") - message = message:gsub("{1}", managers.weapon_factory:get_weapon_name_by_weapon_id(Trading.TradeData.Weapon)) - message = message:gsub("{2}", GoonBase.Network:GetNameFromPeerID(Trading.TradeData.Recipient)) - message = message:gsub("{3}", reason) - local menuOptions = {} - menuOptions[1] = { text = managers.localization:text("Trading_TradeWindowOk"), is_cancel_button = true } - local tradeMenu = SimpleMenu:New(title, message, menuOptions) - tradeMenu:Show() - -end - --- Show Trade Menu when player clicks the trade button -Hooks:Add("TradingAttemptTradeWeapon", "TradingAttemptTradeWeapon_ShowMenu", function(weapon) - Trading:ShowSendToPlayerMenu(weapon.name_localized, weapon) -end) - -Hooks:Add("TradingAttemptTradeWeaponMod", "TradingAttemptTradeWeaponMod_ShowMenu", function(mod) - -- Mods use 'primaries' and 'secondaries' which we use for weapons - -- So we use a custom category to make the trading work with them - mod.category = Trading.Categories.WeaponMod - Trading:ShowSendToPlayerMenu(mod.name_localized, mod) -end) - -Hooks:Add("TradingAttemptTradeMask", "TradingAttemptTradeMask_ShowMenu", function(mask) - - local data = managers.blackmarket:get_mask_slot_data(mask) - if data == nil then - return - end - data.category = "masks" - data.slot = mask.slot - - -- Verify mask parts before sending the mask - -- Some mods get returned to the inventory when selling the mask, stop masks with these on from being traded - -- so that we don't duplicate mods. TODO: Ask Overkill about this before release! - if Trading:VerifyMaskTradability(data) then - Trading:ShowSendToPlayerMenu(mask.name_localized, data) - end - -end) - -Hooks:Add("TradingAttemptTradeMaskPart", "TradingAttemptTradeMaskPart_ShowMenu", function(mod) - - -- Verify mask mod before sending the mask - if Trading:VerifyMaskModTradability(mod) then - Trading:ShowSendToPlayerMenu(mod.name_localized, mod) - end - -end) - -function Trading:ShowSendToPlayerMenu(item_name, item_data) - - if GoonBase.Network:IsMultiplayer() then - - -- Check for tradable peers - if not Trading:HasTradablePeers() then - Trading:NoTradablePeersWindow() - return - end - - -- Show tradable peers - local title = managers.localization:text("Trading_TradeWindowTitle") - local message = managers.localization:text("Trading_TradeWindowMessage"):gsub("{1}", item_name) - local menuOptions = {} - - for k, v in pairs( managers.network:session():peers() ) do - - if Trading.TradablePeers[k] == true then - - local plyData = { - text = v._name, - callback = Trading.SendToPlayerCallback, - data = { - peer = k, - slot = item_data - } - } - table.insert( menuOptions, plyData ) - - end - - end - table.insert( menuOptions, { text = managers.localization:text("Trading_TradeWindowCancel"), is_cancel_button = true } ) - - local tradeMenu = SimpleMenu:New(title, message, menuOptions) - tradeMenu:Show() - - else - Trading:ShowOnlineOnlyWindow() - end - -end - -function Trading.SendToPlayerCallback(data) - - local success, err = pcall(function() - - local category = data.slot.category - Trading.TradeData = { - Recipient = data.peer, - Category = category, - RawData = data - } - - -- Send category to recepient - GoonBase.Network:SendToPeer(data.peer, Trading.Network.MessageType.Category, category) - - -- Weapon trading - if category == Trading.Categories.PrimaryWeapon or category == Trading.Categories.SecondaryWeapon then - - -- Ask if we should send just the weapon or with the mods - local title = managers.localization:text("Trading_TradeWindowTitle") - local message = managers.localization:text("Trading_WeaponSendWithMods") - local menuOptions = {} - menuOptions[1] = { - text = managers.localization:text("Trading_WeaponSendMods"), - callback = Trading.CallbackSendWeaponToPlayer, - data = { - trade_data = data, - send_mods = true - } - } - menuOptions[2] = { - text = managers.localization:text("Trading_WeaponDontSendMods"), - callback = Trading.CallbackSendWeaponToPlayer, - data = { - trade_data = data, - send_mods = false - } - } - local tradeMenu = SimpleMenu:New(title, message, menuOptions) - tradeMenu:Show() - - end - - -- Weapon Mod Trading - if category == Trading.Categories.WeaponMod then - Trading:CallbackSendModToPlayer(data) - end - - -- Mask Trading - if category == Trading.Categories.Mask then - Trading:CallbackSendMaskToPlayer(data) - end - - -- Mask Material/Pattern/Colour Trading - if category == Trading.Categories.MaskMaterial or category == Trading.Categories.MaskPattern or category == Trading.Categories.MaskColour then - Trading:CallbackSendMaskModToPlayer(data) - end - - end) - if not success then Print(err) end - -end - -function Trading.CallbackSendWeaponToPlayer(data) - - local trade_data = data.trade_data - Trading.TradeData.SendWeaponMods = data.send_mods - - local weaponMods = BlackMarketManager:get_mods_on_weapon(trade_data.slot.category, trade_data.slot.slot) - GoonBase.Network:SendToPeer(trade_data.peer, Trading.Network.MessageType.Weapon, trade_data.slot.name) - - if data.send_mods then - for i, modName in pairs(weaponMods) do - GoonBase.Network:SendToPeer(trade_data.peer, Trading.Network.MessageType.WeaponMods, modName) - end - end - - GoonBase.Network:SendToPeer(trade_data.peer, Trading.Network.MessageType.Request, "") - Trading.TradeData.Weapon = trade_data.slot.name - Trading.TradeData.WeaponMods = weaponMods - Trading.TradeData.Slot = trade_data.slot - - -- Show waiting window - Trading:ShowWaitingResponseWindow() - -end - -function Trading:CallbackSendModToPlayer(data) - - -- Send trade data to player - GoonBase.Network:SendToPeer(data.peer, Trading.Network.MessageType.SingleWeaponMod, data.slot.name) - - -- Send request to player - GoonBase.Network:SendToPeer(data.peer, Trading.Network.MessageType.Request, "") - - -- Show waiting window - Trading:ShowWaitingResponseWindow() - -end - -function Trading:CallbackSendMaskToPlayer(data) - - -- Table stuff - local peer = data.peer - data = data.slot - - -- Send modded mask data to player - if data.modded then - GoonBase.Network:SendToPeer(peer, Trading.Network.MessageType.MaskMaterial, data.blueprint.material.id) - GoonBase.Network:SendToPeer(peer, Trading.Network.MessageType.MaskPattern, data.blueprint.pattern.id) - GoonBase.Network:SendToPeer(peer, Trading.Network.MessageType.MaskColour, data.blueprint.color.id) - end - - -- Send mask data to player - GoonBase.Network:SendToPeer(peer, Trading.Network.MessageType.Mask, data.mask_id) - - -- Send request to player - GoonBase.Network:SendToPeer(peer, Trading.Network.MessageType.Request, "") - - -- Show waiting window - Trading:ShowWaitingResponseWindow() - -end - -function Trading:CallbackSendMaskModToPlayer(data) - - -- Table stuff - local peer = data.peer - data = data.slot - - -- Send data to player - if data.category == Trading.Categories.MaskMaterial then - GoonBase.Network:SendToPeer(peer, Trading.Network.MessageType.MaskMaterial, data.name) - end - if data.category == Trading.Categories.MaskPattern then - GoonBase.Network:SendToPeer(peer, Trading.Network.MessageType.MaskPattern, data.name) - end - if data.category == Trading.Categories.MaskColour then - GoonBase.Network:SendToPeer(peer, Trading.Network.MessageType.MaskColour, data.name) - end - - -- Send request - GoonBase.Network:SendToPeer(peer, Trading.Network.MessageType.Request, "") - - -- Show waiting window - Trading:ShowWaitingResponseWindow() - -end - --- Trading Offline Screen -function Trading:ShowOnlineOnlyWindow() - - local title = managers.localization:text("Trading_OnlineOnlyTitle") - local message = managers.localization:text("Trading_OnlineOnlyMessage") - local menuOptions = {} - menuOptions[1] = { text = managers.localization:text("Trading_OnlineOnlyCancel"), is_cancel_button = true } - local tradeMenu = SimpleMenu:New(title, message, menuOptions) - tradeMenu:Show() - -end - --- No Tradable Peers Screen -function Trading:NoTradablePeersWindow() - - local title = managers.localization:text("Trading_NoPeersTitle") - local message = managers.localization:text("Trading_NoPeersMessage") - local menuOptions = {} - menuOptions[1] = { text = managers.localization:text("Trading_NoPeersCancel"), is_cancel_button = true } - local tradeMenu = SimpleMenu:New(title, message, menuOptions) - tradeMenu:Show() - -end - --- Trade Wait Screen -function Trading:ShowWaitingResponseWindow() - - local title = managers.localization:text("Trading_WaitingResponseTitle") - local message = managers.localization:text("Trading_WaitingResponseMessage") - message = message:gsub("{1}", GoonBase.Network:GetNameFromPeerID(Trading.TradeData.Recipient)) - local menuOptions = {} - menuOptions[1] = { - text = managers.localization:text("Trading_WaitingResponseCancel"), - callback = Trading.WaitingResponseCancel, - } - Trading.ActiveTradeWindow = SimpleMenu:New(title, message, menuOptions) - Trading.ActiveTradeWindow.dialog_data.indicator = true - Trading.ActiveTradeWindow:Show() - -end - --- Trade Cancelling -function Trading.WaitingResponseCancel() - - -- Close trade waiting screen - Trading:CloseActiveWindow() - - -- Tell recipient to clear trade data - GoonBase.Network:SendToPeer(Trading.TradeData.Recipient, Trading.Network.MessageType.Cancel, "") - - -- Clear local trade data - Trading.TradeData = {} - -end - -function Trading:HandleTradeCancelled(sender) - - -- Remove trade response window - Trading:CloseActiveWindow() - - -- Show trade cancelled window - local title = managers.localization:text("Trading_OtherCancelledTitle") - local message = managers.localization:text("Trading_OtherCancelledMessage") - message = message:gsub("{1}", GoonBase.Network:GetNameFromPeerID(Trading.TradeData.Trader)) - local menuOptions = {} - menuOptions[1] = { - text = managers.localization:text("Trading_OtherCancelledAccept"), - is_cancel_button = true - } - Trading.ActiveTradeWindow = SimpleMenu:New(title, message, menuOptions) - Trading.ActiveTradeWindow.dialog_data.indicator = true - Trading.ActiveTradeWindow:Show() - - -- Clear local trade data - Trading.TradeData = {} - -end - --- Windows -function Trading:CloseActiveWindow() - if Trading.ActiveTradeWindow ~= nil then - managers.system_menu:close(Trading.ActiveTradeWindow.dialog_data.id) - Trading.ActiveTradeWindow.visible = false - Trading.ActiveTradeWindow = nil - end -end - --- Weapon Inventory -function Trading:_GetWeaponTweakData(weapon) - return tweak_data.weapon[weapon] -end - -function Trading:AddWeaponToInventory(weapon, mods) - - local category = Trading.TradeData.Category or "primaries" - local freeSlot = managers.blackmarket:_get_free_weapon_slot(category) - - if freeSlot ~= nil then - - managers.blackmarket:on_buy_weapon_platform(category, weapon, freeSlot, true) - - if mods ~= nil then - - -- Keep track of mods we can't equip - local banned_mods = {} - - -- Attach mods - for k, v in pairs(mods) do - local factory = tweak_data.weapon.factory.parts[v] - if factory then - - -- Attach weapons for DLC we own, add it to the inventory for mods we dont - if factory.dlc ~= nil and not managers.dlc:has_dlc(factory.dlc) then - banned_mods[v] = factory.dlc - managers.blackmarket:add_to_inventory(factory.dlc, "weapon_mods", v, true) - else - managers.blackmarket:buy_and_modify_weapon(category, freeSlot, "normal", v, true, true) - end - - end - end - - -- Warn us about mods we can't use - for k, v in pairs(banned_mods) do - Trading:ShowModDLCNotOwned(tweak_data.weapon.factory.parts[k].name_id, tweak_data.weapon.factory.parts[k].dlc) - end - - end - - end - -end - -function Trading:ShowModDLCNotOwned(mod, dlc) - - mod = managers.localization:text(mod) - dlc = Trading.DLCNames[dlc] or ("error: " .. dlc) - - local title = managers.localization:text("Trading_TradeWindowTitle") - local message = managers.localization:text("Trading_WeaponModRemovedDLC") - message = message:gsub("{1}", mod) - message = message:gsub("{2}", dlc) - local menuOptions = {} - menuOptions[1] = { text = managers.localization:text("Trading_TradeWindowOk"), is_cancel_button = true } - local tradeMenu = SimpleMenu:New(title, message, menuOptions) - tradeMenu:Show() - -end - -function Trading:RemoveWeaponFromInventory() - - local category = Trading.TradeData.Slot.category - local slot = Trading.TradeData.Slot.slot - local remove_mods = not Trading.TradeData.SendWeaponMods - - -- Remove weapon and mods - local success, err = pcall(function() - managers.blackmarket:on_traded_weapon(category, slot, remove_mods) - end) - if not success then Print(err) end - - -- Reload gui - if Trading.BlackMarketGUI ~= nil then - Trading.BlackMarketGUI:reload() - end - -end - -function Trading:RemoveModFromInventory() - - managers.blackmarket:on_traded_mod(Trading.TradeData.RawData.slot.name) - -end - -function Trading:AddTradedModToInventory() - - local success, err = pcall(function() - - local mod = Trading.TradeData.WeaponMod - - -- Show message about mod - local factory = tweak_data.weapon.factory.parts[mod] - if factory then - - local mod_name = managers.localization:text(factory.name_id) - local ply_name = GoonBase.Network:GetNameFromPeerID(Trading.TradeData.Trader) - - -- Attach weapons for DLC we own, add it to the inventory for mods we dont - if factory.dlc ~= nil and not managers.dlc:has_dlc(factory.dlc) then - - local dlc_name = Trading.DLCNames[factory.dlc] or ("error: " .. factory.dlc) - - -- DLC is not owned, show different message - local title = managers.localization:text("Trading_TradeWindowTitle") - local message = managers.localization:text("Trading_WeaponModReceivedNoDLC") - message = message:gsub("{1}", mod_name) - message = message:gsub("{2}", ply_name) - message = message:gsub("{3}", dlc_name) - local menuOptions = {} - menuOptions[1] = { text = managers.localization:text("Trading_TradeWindowOk"), is_cancel_button = true } - local tradeMenu = SimpleMenu:New(title, message, menuOptions) - tradeMenu:Show() - - else - - -- Show default message - local title = managers.localization:text("Trading_TradeWindowTitle") - local message = managers.localization:text("Trading_WeaponModReceived") - message = message:gsub("{1}", mod_name) - message = message:gsub("{2}", ply_name) - local menuOptions = {} - menuOptions[1] = { text = managers.localization:text("Trading_TradeWindowOk"), is_cancel_button = true } - local tradeMenu = SimpleMenu:New(title, message, menuOptions) - tradeMenu:Show() - - end - - end - - -- Add mod to inventory - managers.blackmarket:on_received_traded_mod( Trading.TradeData.WeaponMod ) - - end) - if not success then Print(err) end - -end - -function Trading:AddTradedMaskToInventory() - - local success, err = pcall(function() - - local mask_id = Trading.TradeData.Mask - - if Trading.TradeData.MaskMods ~= nil then - - -- Adding mods to mask - local material = Trading.TradeData.MaskMods.Material - local pattern = Trading.TradeData.MaskMods.Pattern - local color = Trading.TradeData.MaskMods.Colour - - -- Check if user has all DLC for mask - local dlc_check = managers.blackmarket:has_all_dlc_for_mask_and_parts(mask_id, material, pattern, color) - if type(dlc_check) == "table" then - - -- User doesn't have all DLC, add items to inventory instead - managers.blackmarket:add_traded_mask_to_inventory(mask_id) - managers.blackmarket:add_traded_mask_part_to_inventory(material, Trading.Categories.MaskMaterial) - managers.blackmarket:add_traded_mask_part_to_inventory(pattern, Trading.Categories.MaskPattern) - managers.blackmarket:add_traded_mask_part_to_inventory(color, Trading.Categories.MaskColour) - - -- Localization strings - local mask_name = managers.blackmarket:get_mask_name( Trading.TradeData.Mask ) - local player_name = GoonBase.Network:GetNameFromPeerID( Trading.TradeData.Trader ) - local missing_dlcs = "" - - -- Show DLC warning - for k, v in pairs( dlc_check ) do - if missing_dlcs ~= "" then - missing_dlcs = missing_dlcs .. ", or " - end - missing_dlcs = (missing_dlcs .. "'{1}'"):gsub("{1}", Trading.DLCNames[k] or k) - end - Trading:ShowMaskTradeDLCWarning(mask_name, player_name, missing_dlcs) - - return - end - - -- Add modded mask to inventory - managers.blackmarket:add_traded_modded_mask_to_free_slot(mask_id, material, pattern, color) - - else - - -- Only sending mask, add to inventory - managers.blackmarket:add_traded_mask_to_inventory(mask_id) - - -- Check player owns DLC - local missing_dlc = managers.blackmarket:has_all_dlc_for_mask(mask_id) - if type(missing_dlc) == "string" then - local mask_name = managers.blackmarket:get_mask_name( Trading.TradeData.Mask ) - local player_name = GoonBase.Network:GetNameFromPeerID( Trading.TradeData.Trader ) - local dlc_name = ("'{1}'"):gsub("{1}", Trading.DLCNames[missing_dlc] or missing_dlc) - Trading:ShowMaskTradeDLCWarning(mask_name, player_name, dlc_name) - end - - end - - end) - if not success then Print(err) end - -end - -function Trading:AddTradedMaskModToInventory() - - local success, err = pcall(function() - - -- Get mod ID and check mod is valid - local mod_id = nil - local category = Trading.TradeData.Category - - if category == Trading.Categories.MaskMaterial then - mod_id = Trading.TradeData.MaskMods.Material - end - if category == Trading.Categories.MaskPattern then - mod_id = Trading.TradeData.MaskMods.Pattern - end - if category == Trading.Categories.MaskColour then - mod_id = Trading.TradeData.MaskMods.Colour - end - if mod_id == nil then - return - end - - -- Add mod to inventory - managers.blackmarket:add_traded_mask_part_to_inventory(mod_id, category) - - -- Warn about DLC - local missing_dlc = managers.blackmarket:has_all_dlc_for_mask_mod(mod_id, category) - if type(missing_dlc) == "string" then - local mod_name = managers.blackmarket:get_mask_mod_name( mod_id, category ) - local player_name = GoonBase.Network:GetNameFromPeerID( Trading.TradeData.Trader ) - local dlc_name = ("'{1}'"):gsub("{1}", Trading.DLCNames[missing_dlc] or missing_dlc) - Trading:ShowMaskTradeDLCWarning(mod_name, player_name, dlc_name) - end - - end) - if not success then Print(err) end - -end - -function Trading:ShowMaskTradeDLCWarning(mask_name, player_name, missing_dlcs) - - local title = managers.localization:text("Trading_TradeWindowTitle") - local message = managers.localization:text("Trading_MaskReceivedNoDLC") - message = message:gsub("{1}", mask_name) - message = message:gsub("{2}", player_name) - message = message:gsub("{3}", missing_dlcs) - local menuOptions = {} - menuOptions[1] = { - text = managers.localization:text("Trading_MaskReceivedAccept"), - is_cancel_button = true - } - local tradeMenu = SimpleMenu:New(title, message, menuOptions) - tradeMenu:Show() - -end - -function Trading:VerifyMaskTradability(data) - - -- Items which are untradable - local untradable = nil - - -- Check mask itself is fine to trade - local mask_data = managers.blackmarket:get_mask_data( data.mask_id ) - if mask_data == nil then - return - end - if mask_data.dlc ~= nil and mask_data.value == 0 then - if untradable == nil then untradable = {} end - untradable["masks"] = data.mask_id - end - - -- Check mask components are ok - if data.blueprint ~= nil then - - -- Material - local material_data = managers.blackmarket:get_mask_mod_data( data.blueprint.material.id, Trading.Categories.MaskMaterial ) - if material_data ~= nil and material_data.dlc ~= nil and material_data.value == 0 then - if untradable == nil then untradable = {} end - untradable["materials"] = data.blueprint.material.id - end - - -- Pattern - local pattern_data = managers.blackmarket:get_mask_mod_data( data.blueprint.pattern.id, Trading.Categories.MaskPattern ) - if pattern_data ~= nil and pattern_data.dlc ~= nil and pattern_data.value == 0 then - if untradable == nil then untradable = {} end - untradable["textures"] = data.blueprint.pattern.id - end - - -- Colour - local color_data = managers.blackmarket:get_mask_mod_data( data.blueprint.color.id, Trading.Categories.MaskColour ) - if color_data ~= nil and color_data.dlc ~= nil and color_data.value == 0 then - if untradable == nil then untradable = {} end - untradable["colors"] = data.blueprint.color.id - end - - end - - -- Show untradables - if untradable ~= nil then - Trading:ShowMaskTradabilityMessage(managers.blackmarket:get_mask_name(data.mask_id), untradable) - return false - end - - -- Mask is clear, good to trade - return true - -end - -function Trading:VerifyMaskModTradability(mod, category) - - local mod_data = managers.blackmarket:get_mask_mod_data( mod.name, mod.category ) - if mod_data ~= nil and mod_data.dlc ~= nil and mod_data.value == 0 then - Trading:ShowMaskModTradabilityMessage(mod.name_localized) - return false - end - return true - -end - -function Trading:ShowMaskTradabilityMessage(mask_name, items) - - local item_names = "" - for k, v in pairs( items ) do - if item_names ~= "" then - item_names = item_names .. ", and " - end - item_names = (item_names .. "'{1}'"):gsub("{1}", managers.blackmarket:get_mask_mod_name(v, k)) - end - - local title = managers.localization:text("Trading_TradeWindowTitle") - local message = managers.localization:text("Trading_UntradableMask") - message = message:gsub("{1}", mask_name) - message = message:gsub("{2}", item_names) - local menuOptions = {} - menuOptions[1] = { - text = managers.localization:text("Trading_UntradableMaskAccept"), - is_cancel_button = true - } - local tradeMenu = SimpleMenu:New(title, message, menuOptions) - tradeMenu:Show() - -end - -function Trading:ShowMaskModTradabilityMessage(mod_name) - - local title = managers.localization:text("Trading_TradeWindowTitle") - local message = managers.localization:text("Trading_UntradableMaskMod") - message = message:gsub("{1}", mod_name) - local menuOptions = {} - menuOptions[1] = { - text = managers.localization:text("Trading_UntradableMaskModAccept"), - is_cancel_button = true - } - local tradeMenu = SimpleMenu:New(title, message, menuOptions) - tradeMenu:Show() - -end - --- Fix for people who traded away their preferred character masks -function Trading:_FixPreferredCharacterMask() - - if GoonBase.Options.Trading and not GoonBase.Options.Trading.FixedPreferredCharacterMask then - - Print("[Trading] Checking if player traded away character locked mask...") - - local index = managers.blackmarket._global.crafted_items.masks[1] - local requires_reset = false - - if index then - if index.mask_id ~= "character_locked" then - requires_reset = true - end - else - requires_reset = true - end - - if requires_reset then - - Print("[Trading] Charcter locked mask not in proper place, fixing, mask in its place will be lost...") - - if not index then - index = {} - end - - index.mask_id = "character_locked" - index.global_value = "normal" - index.modded = false - index.blueprint = { - ["color"] = { - id = "nothing", - global_value = "normal", - }, - ["material"] = { - id = "plastic", - global_value = "normal", - }, - ["pattern"] = { - id = "no_color_no_material", - global_value = "normal", - }, - } - - managers.blackmarket._global.crafted_items.masks[1] = index - - else - Print("[Trading] Mask is in proper place, skipping fix") - end - - GoonBase.Options.Trading.FixedPreferredCharacterMask = true - GoonBase.Options:Save() - - end - -end --- END OF FILE diff --git a/GoonBase/mods/train_heist_plans.lua b/GoonBase/mods/train_heist_plans.lua deleted file mode 100644 index 062d93a..0000000 --- a/GoonBase/mods/train_heist_plans.lua +++ /dev/null @@ -1,122 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - --- Mod Definition -local Mod = class( BaseMod ) -Mod.id = "TrainHeistPlans" -Mod.Name = "Separate Train Heist" -Mod.Desc = "The train heist from the Armoured Transport DLC is available as a separate heist.\nWARNING: Will cause problems with people who do not have the mod." -Mod.Requirements = { "ExtendedInventory" } -Mod.Incompatibilities = {} - -Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() - GoonBase.Mods:RegisterMod( Mod ) -end) - -if not Mod:IsEnabled() then - return -end - -local Localization = GoonBase.Localization -Localization.TrainHeist_PlansInv = "Train Intel" -Localization.TrainHeist_PlansInvDesc = [[Details of a train transporting an experimental turret. This unlocks the Train Transport heist in Crime.net while you have intel in reserve. - -Found in an Armoured Transport. Will be consumed upon successful completion of the heist.]] - -Hooks:Add("NarrativeTweakDataInit", "NarrativeTweakDataInit_" .. Mod:ID(), function(data) - - local ExtendedInv = _G.GoonBase.ExtendedInventory - if ExtendedInv == nil then - return - end - - if ExtendedInv:HasItem("train_heist_plans") then - - data.jobs.arm_for_prof = deep_clone( data.jobs.arm_for ) - data.jobs.arm_for_prof.contact = "bain" - data.jobs.arm_for_prof.professional = true - - table.insert( data._jobs_index, "arm_for_prof" ) - table.insert( data.jobs.arm_wrapper.job_wrapper, "arm_for_prof" ) - - data:set_job_wrappers() - - end - -end) - -Hooks:Add("LevelsTweakDataInit", "LevelsTweakDataInit_" .. Mod:ID(), function(data) - - local ExtendedInv = _G.GoonBase.ExtendedInventory - if ExtendedInv == nil then - return - end - - if ExtendedInv:HasItem("train_heist_plans") then - - data.arm_for_prof = deep_clone( data.arm_for ) - data.arm_for_prof.bonus_escape = false - data.arm_for_prof.static_experience = { - 60000, - 70000, - 80000, - 90000, - 100000 - } - - table.insert(data._level_index, "arm_for_prof") - - end - -end) - -Hooks:Add("ExtendedInventoryInitialized", "ExtendedInventoryInitialized_" .. Mod:ID(), function() - - local ExtendedInv = _G.GoonBase.ExtendedInventory - if ExtendedInv == nil then - return - end - - ExtendedInv:RegisterItem({ - id = "train_heist_plans", - name = "TrainHeist_PlansInv", - desc = "TrainHeist_PlansInvDesc", - texture = "guis/dlcs/dlc1/textures/pd2/mission_briefing/assets/train_01", - hide_when_none_in_stock = true, - }) - -end) - -Hooks:Add("JobManagerOnSetNextInteruptStage", "JobManagerOnSetNextInteruptStage_" .. Mod:ID(), function(job_manager, interupt) - - if interupt == "arm_for" then - - managers.job:set_next_interupt_stage(nil) - - local ExtendedInv = _G.GoonBase.ExtendedInventory - if ExtendedInv ~= nil then - ExtendedInv:AddItem("train_heist_plans", 1) - end - - end - -end) - -Hooks:Add("GameStateMachineChangeStateByName", "GameStateMachineChangeStateByName_" .. Mod:ID(), function(gsm, state_name, params) - - if state_name == "victoryscreen" then - - local level_id = Global.game_settings.level_id - if level_id == "arm_for" or level_id == "arm_for_prof" then - local ExtendedInv = _G.GoonBase.ExtendedInventory - if ExtendedInv ~= nil then - ExtendedInv:TakeItem("train_heist_plans", 1) - end - end - - end - -end) --- END OF FILE diff --git a/GoonBase/mods/weapon_customization.lua b/GoonBase/mods/weapon_customization.lua deleted file mode 100644 index 6406688..0000000 --- a/GoonBase/mods/weapon_customization.lua +++ /dev/null @@ -1,647 +0,0 @@ ----------- --- Payday 2 GoonMod, Public Release Beta 2, built on 1/23/2015 10:01:12 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - --- Mod Definition -local Mod = class( BaseMod ) -Mod.id = "WeaponCustomization" -Mod.Name = "[BETA] Weapon Customization" -Mod.Desc = "Visually customize your weapons using materials, patterns, and colour swatches" -Mod.Requirements = {} -Mod.Incompatibilities = {} - -Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() - GoonBase.Mods:RegisterMod( Mod ) -end) - -if not Mod:IsEnabled() then - return -end - --- Weapon Customization -GoonBase.WeaponCustomization = GoonBase.WeaponCustomization or {} -local WeaponCustomization = GoonBase.WeaponCustomization -WeaponCustomization.MenuId = "goonbase_weapon_customization_menu" -WeaponCustomization._update_queue = {} - -WeaponCustomization._default_part_visual_blueprint = { - ["materials"] = "no_material", - ["textures"] = "no_color_no_material", - ["colors"] = "white_solid", -} - -WeaponCustomization._mod_overrides_download_location = "https://github.com/JamesWilko/GoonMod/archive/WeaponCustomizerModOverrides.zip" - --- Load extras -SafeDoFile( GoonBase.Path .. "mods/weapon_customization_menus.lua" ) -SafeDoFile( GoonBase.Path .. "mods/weapon_customization_part_data.lua" ) - --- Localization -local Localization = GoonBase.Localization -Localization.Options_WeaponCustomizationName = "Weapon Customization" -Localization.Options_WeaponCustomizationDesc = "Weapon Customization Options" - -Localization.WeaponCustomization_DownloadModOverridesManual = "Download Mod Overrides (Manual Install)" -Localization.WeaponCustomization_DownloadModOverridesManualDesc = [[Download the required mod overrides to use the Weapon Customization mod, opens in your browser. -Place the 'GoonModWeaponCustomizer' folder into your mod_overrides and then restart your game.]] - -Localization.WeaponCustomization_ClearDataButton = "Clear Weapon Customization Data" -Localization.WeaponCustomization_ClearDataButtonDesc = "Erase all of your weapon customization data from your Payday 2 save file" - -Localization.WeaponCustomization_ClearDataTitle = "Clear Weapon Customization Data" -Localization.WeaponCustomization_ClearDataMessage = [[This will erase all weapon customization data from your save game. -You will lose all of your modified weapon visuals, but you will not lose the weapons themselves. - -You may need to restart your game, or load a mission, to fully clear your texture cache.]] -Localization.WeaponCustomization_ClearDataAccept = "Clear Data" -Localization.WeaponCustomization_ClearDataCancel = "Cancel" - -Localization.WeaponCustomization_PrintAllPartNames = "Output All Weapon Part Names" -Localization.WeaponCustomization_PrintAllPartNamesDesc = "Outputs all weapon part names to a CSV file" - -Localization.bm_mtl_no_material = "No Material" - --- Options -if GoonBase.Options.WeaponCustomization == nil then - GoonBase.Options.WeaponCustomization = {} - GoonBase.Options.WeaponCustomization.Color1R = 1 - GoonBase.Options.WeaponCustomization.Color1G = 1 - GoonBase.Options.WeaponCustomization.Color1B = 1 - GoonBase.Options.WeaponCustomization.Color2R = 1 - GoonBase.Options.WeaponCustomization.Color2G = 1 - GoonBase.Options.WeaponCustomization.Color2B = 1 - GoonBase.Options.WeaponCustomization.Pattern = 1 - GoonBase.Options.WeaponCustomization.Material = 1 - GoonBase.Options.WeaponCustomization.HideDiffuse = false - GoonBase.Options.WeaponCustomization.HideNormal = false - GoonBase.Options.WeaponCustomization.TempShownOverridesNotInstalled = false -end - --- Menu -Hooks:Add("MenuManagerSetupCustomMenus", "MenuManagerSetupCustomMenus_" .. Mod:ID(), function(menu_manager, menu_nodes) - GoonBase.MenuHelper:NewMenu( WeaponCustomization.MenuId ) -end) - -Hooks:Add("MenuManagerSetupGoonBaseMenu", "MenuManagerSetupGoonBaseMenu_" .. Mod:ID(), function(menu_manager, menu_nodes) - - -- Submenu Button - GoonBase.MenuHelper:AddButton({ - id = "weapon_customization_menu_button", - title = "Options_WeaponCustomizationName", - desc = "Options_WeaponCustomizationDesc", - next_node = WeaponCustomization.MenuId, - menu_id = "goonbase_options_menu", - }) - - -- Menu - MenuCallbackHandler.wc_download_mod_overrides = function(this, item) - if SystemInfo:platform() == Idstring("WIN32") then - os.execute( "explorer " .. WeaponCustomization._mod_overrides_download_location ) - end - end - - MenuCallbackHandler.clear_weapon_visual_customizations = function(this, item) - WeaponCustomization:ShowClearDataConfirmation() - end - - GoonBase.MenuHelper:AddButton({ - id = "weapon_customization_download_mod_overrides", - title = "WeaponCustomization_DownloadModOverridesManual", - desc = "WeaponCustomization_DownloadModOverridesManualDesc", - callback = "wc_download_mod_overrides", - menu_id = WeaponCustomization.MenuId, - priority = 100, - }) - - GoonBase.MenuHelper:AddDivider({ - id = "weapon_customization_divider1", - menu_id = WeaponCustomization.MenuId, - size = 16, - priority = 99, - }) - - GoonBase.MenuHelper:AddButton({ - id = "weapon_customization_clear_data", - title = "WeaponCustomization_ClearDataButton", - desc = "WeaponCustomization_ClearDataButtonDesc", - callback = "clear_weapon_visual_customizations", - menu_id = WeaponCustomization.MenuId, - priority = 98, - }) - -end) - -Hooks:Add("MenuManagerBuildCustomMenus", "MenuManagerBuildCustomMenus_" .. Mod:ID(), function(menu_manager, mainmenu_nodes) - local menu_id = WeaponCustomization.MenuId - local data = { - area_bg = "none" - } - mainmenu_nodes[menu_id] = GoonBase.MenuHelper:BuildMenu( menu_id, data ) -end) - --- Hooks -Hooks:Add("BlackMarketGUIOnPreviewWeapon", "BlackMarketGUIOnPreviewWeapon_WeaponCustomization", function(gui, data) - if not WeaponCustomization._is_previewing then - WeaponCustomization._is_previewing = { - ["previewing"] = true, - ["data"] = data, - } - end -end) - -Hooks:Add("MenuSceneManagerSpawnedItemWeapon", "MenuSceneManagerSpawnedItemWeapon_" .. Mod:ID(), function(factory_id, blueprint, texture_switches, spawned_unit) - - WeaponCustomization._menu_weapon_preview_unit = spawned_unit - - if WeaponCustomization._is_previewing then - local data = WeaponCustomization._is_previewing["data"] - WeaponCustomization:LoadCurrentWeaponCustomization( data.category, data.slot ) - WeaponCustomization._is_previewing = nil - end - -end) - -Hooks:Add("NewRaycastWeaponBasePostAssemblyComplete", "NewRaycastWeaponBasePostAssemblyComplete_WeaponCustomization", function(weapon, clbk, parts, blueprint) - WeaponCustomization:LoadEquippedWeaponCustomizations( weapon ) -end) - -Hooks:Add("PlayerStandardStartActionEquipWeapon", "PlayerStandardStartActionEquipWeapon_WeaponCustomization", function(ply, t) - if managers.player:local_player() then - WeaponCustomization:LoadEquippedWeaponCustomizations( managers.player:local_player():inventory():equipped_unit():base() ) - end -end) - -Hooks:Add("PlayerStandardStartMaskUp", "PlayerStandardStartMaskUp_WeaponCustomization", function(ply, data) - if managers.player:local_player() then - WeaponCustomization:LoadEquippedWeaponCustomizations( managers.player:local_player():inventory():equipped_unit():base() ) - end -end) - -Hooks:Add("BlackMarketGUIOnPopulateMaskMods", "BlackMarketGUIOnPopulateMaskMods_WeaponCustomization", function(gui, data) - - -- Create "no material" data - if not tweak_data.blackmarket.materials.no_material then - - tweak_data.blackmarket.materials.no_material = {} - tweak_data.blackmarket.materials.no_material.name_id = "bm_mtl_no_material" - tweak_data.blackmarket.materials.no_material.texture = "units/payday2/matcaps/matcap_plastic_df" - tweak_data.blackmarket.materials.no_material.value = 0 - - end - - -- Inject "no material" material - local no_material_index = nil - for k, v in ipairs( data.on_create_data ) do - if v.id == "no_material" then - no_material_index = k - break - end - end - - if no_material_index then - table.remove( data.on_create_data, no_material_index ) - end - - if data.category == "materials" then - - local clear_material = deep_clone( data.on_create_data[1] ) - clear_material.id = "no_material" - clear_material.bitmap_texture_override = "plastic" - clear_material.free_of_charge = true - - table.insert( data.on_create_data, 1, clear_material ) - - end - -end) - --- Functions -function WeaponCustomization:AddCustomizablePart( part_id ) - local tbl = clone( WeaponCustomization._default_part_visual_blueprint ) - tbl.id = part_id - tbl.modifying = false - table.insert( managers.blackmarket._customizing_weapon_parts, tbl ) -end - -function WeaponCustomization:CreateCustomizablePartsList( weapon ) - - -- Clear weapon parts - managers.blackmarket._customizing_weapon_parts = {} - local blueprint_parts = {} - - -- Add blueprint parts - for k, v in ipairs( weapon.blueprint ) do - - -- Add blueprint part - WeaponCustomization:AddCustomizablePart( v ) - blueprint_parts[v] = true - - -- Check if part has adds - local part_data = tweak_data.weapon.factory.parts[v] - if part_data and part_data.adds then - for x, y in pairs( part_data.adds ) do - WeaponCustomization:AddCustomizablePart( y ) - blueprint_parts[y] = true - end - end - - end - - -- Add weapon extra part adds - local weapon_data = tweak_data.weapon.factory[ weapon.factory_id ] - if weapon_data and weapon_data.adds then - for k, v in pairs( weapon_data.adds ) do - if blueprint_parts[k] then - for x, y in pairs( v ) do - WeaponCustomization:AddCustomizablePart( y ) - end - end - end - end - -end - -function WeaponCustomization:QueueWeaponUpdate( material_id, pattern_id, tint_color_a, tint_color_b, parts_table ) - - if not self._update_queue then - self._update_queue = {} - end - - local data = { - ["material"] = material_id, - ["pattern"] = pattern_id, - ["color_a"] = tint_color_a, - ["color_b"] = tint_color_b, - ["parts"] = parts_table, - } - table.insert( self._update_queue, data ) - -end - -function WeaponCustomization:DequeueUpdate() - - if not self._update_queue then - Print("[Error] Could not dequeue as the update queue does not exist") - return - end - - if #self._update_queue > 0 then - local data = self._update_queue[1] - WeaponCustomization:UpdateWeapon( data["material"], data["pattern"], data["color_a"], data["color_b"], data["parts"] ) - table.remove( self._update_queue, 1 ) - end - -end - -function WeaponCustomization:UpdateWeaponPartsWithMaskMod( data ) - WeaponCustomization:UpdateWeaponPartsWithMod( data.category, data.mods.id ) -end - -function WeaponCustomization:UpdateWeaponPartsWithMod( category, mod_id, parts_table, disable_saving ) - - if managers.blackmarket._customizing_weapon and managers.blackmarket._customizing_weapon_data and managers.blackmarket._selected_weapon_parts then - - -- Set selected part - if managers.blackmarket._selected_weapon_parts[ category ] then - managers.blackmarket._selected_weapon_parts[ category ] = mod_id - end - - -- Get parts to modify - if not parts_table then - parts_table = {} - for k, v in ipairs( managers.blackmarket._customizing_weapon_parts ) do - if v.modifying then - parts_table[v.id] = v - end - end - end - - -- Modify parts - for k, v in pairs( parts_table ) do - - -- Update category mod - if v[ category ] then - v[ category ] = mod_id - end - - -- Update part visuals - local color_data = tweak_data.blackmarket.colors[ v["colors"] ] - WeaponCustomization:UpdateWeapon( v["materials"], v["textures"], color_data.colors[1], color_data.colors[2], { [v.id] = true } ) - - end - - -- Save current weapon customization - if not disable_saving then - WeaponCustomization:SaveCurrentWeaponCustomization() - end - - end - -end - -function WeaponCustomization:UpdateWeaponUsingOptions() - - local opts = GoonBase.Options.WeaponCustomization - local material = WeaponCustomization._materials_lookup[ opts.Material ] - local pattern = WeaponCustomization._patterns_lookup[ opts.Pattern ] - local tint_color_a = Color( opts.Color1R, opts.Color1G, opts.Color1B ) - local tint_color_b = Color( opts.Color2R, opts.Color2G, opts.Color2B ) - - if opts.Pattern == 1 then - if opts.Material == 1 then - pattern = "no_color_no_material" - else - pattern = "no_color_full_material" - end - end - - self:UpdateWeapon( material, pattern, tint_color_a, tint_color_b ) - -end - -function WeaponCustomization:UpdateWeapon( material_id, pattern_id, tint_color_a, tint_color_b, parts_table, unit_override ) - - if self._requesting then - Print("[Error] Could not update weapon customization, already updating current materials") - return - end - - -- Find weapon - local weapon_base = unit_override and unit_override:base() or nil - if not weapon_base then - - if managers.player:local_player() then - weapon_base = managers.player:local_player():inventory():equipped_unit():base() - end - if self._menu_weapon_preview_unit and alive( self._menu_weapon_preview_unit ) then - weapon_base = self._menu_weapon_preview_unit:base() - end - - end - - if not weapon_base then - Print("[Error] Could not update weapon customization, no weapon unit") - return - end - - -- Defaults - material_id = material_id or "no_material" - pattern_id = pattern_id or "no_color_no_material" - if material_id ~= "no_material" and pattern_id == "no_color_no_material" then - pattern_id = "no_color_full_material" - end - if material_id == "no_material" then - pattern_id = "no_color_no_material" - end - tint_color_a = tint_color_a or Color(1, 1, 1) - tint_color_b = tint_color_b or Color(1, 1, 1) - - -- Callbacks - local texture_load_result_clbk = callback(self, self, "clbk_texture_loaded") - - self._materials = {} - self._textures = {} - - -- Find materials - for k, v in pairs( weapon_base._parts ) do - if v.unit and ( (parts_table and parts_table[k]) or not parts_table ) then - - local materials = v.unit:get_objects_by_type(Idstring("material")) - for _, m in ipairs(materials) do - if m:variable_exists(Idstring("tint_color_a")) then - table.insert(self._materials, m) - end - end - - end - end - - -- Material - local old_reflection = self._textures.reflection and self._textures.reflection.name - local material_amount = 1 - if tweak_data.blackmarket.materials[material_id] then - - local material_data = tweak_data.blackmarket.materials[material_id] - local reflection = Idstring(material_data.texture) - if old_reflection ~= reflection then - self._textures.reflection = { - name = reflection, - texture = false, - ready = false - } - end - material_amount = material_data.material_amount or 1 - - end - - -- Pattern - local old_pattern = self._textures.pattern and self._textures.pattern.name - if tweak_data.blackmarket.textures[pattern_id] then - - local pattern = Idstring(tweak_data.blackmarket.textures[pattern_id].texture) - if old_pattern ~= pattern then - self._textures.pattern = { - name = pattern, - texture = false, - ready = false - } - end - - end - - -- Set Textures - for _, material in ipairs(self._materials) do - material:set_variable(Idstring("tint_color_a"), tint_color_a) - material:set_variable(Idstring("tint_color_b"), tint_color_b) - material:set_variable(Idstring("material_amount"), material_amount) - end - - -- Load - self._requesting = true - - for tex_id, texture_data in pairs(self._textures) do - if not texture_data.ready then - texture_data.ready = true - for _, material in ipairs(self._materials) do - Application:set_material_texture(material, Idstring(tex_id == "pattern" and "material_texture" or "reflection_texture"), texture_data.name, Idstring("normal"), 0) - end - end - end - - self._requesting = nil - -end - -function WeaponCustomization:clbk_texture_loaded(tex_name) - - for tex_id, texture_data in pairs(self._textures) do - if not texture_data.ready and tex_name == texture_data.name then - texture_data.ready = true - for _, material in ipairs(self._materials) do - Application:set_material_texture(material, Idstring(tex_id == "pattern" and "material_texture" or "reflection_texture"), tex_name, Idstring("normal"), 0) - end - end - end - -end - -function WeaponCustomization:SaveCurrentWeaponCustomization() - - if not managers.blackmarket._customizing_weapon or not managers.blackmarket._customizing_weapon_data or not managers.blackmarket._selected_weapon_parts then - Print("[Error] Could not save weapon customization, no customization data") - return - end - - -- Get weapon - local weapon_category = managers.blackmarket._customizing_weapon_data.category - local weapon_slot = managers.blackmarket._customizing_weapon_data.slot - local weapon = managers.blackmarket._global.crafted_items[weapon_category][weapon_slot] - - if not weapon then - Print("[Error] Could not save weapon customization, no weapon found in category '", weapon_category, "' slot '", weapon_slot, "'") - return - end - - -- Setup weapon visual customization save - if not weapon.visual_blueprint then - weapon.visual_blueprint = {} - for k, v in ipairs( weapon.blueprint ) do - weapon.visual_blueprint[v] = clone( WeaponCustomization._default_part_visual_blueprint ) - end - end - - -- Get modified parts - local parts = {} - for k, v in ipairs( managers.blackmarket._customizing_weapon_parts ) do - if v.modifying then - parts[v.id] = true - end - end - - -- Update visual customization - for k, v in pairs( parts ) do - - local mat = managers.blackmarket._selected_weapon_parts["materials"] - local tex = managers.blackmarket._selected_weapon_parts["textures"] - local col = managers.blackmarket._selected_weapon_parts["colors"] - - if not weapon.visual_blueprint[k] then - weapon.visual_blueprint[k] = clone( WeaponCustomization._default_part_visual_blueprint ) - end - - weapon.visual_blueprint[k]["materials"] = mat - weapon.visual_blueprint[k]["textures"] = tex - weapon.visual_blueprint[k]["colors"] = col - - end - -end - -function WeaponCustomization:LoadCurrentWeaponCustomization( category, slot ) - - -- Get weapon - local weapon_category = category or managers.blackmarket._customizing_weapon_data.category - local weapon_slot = slot or managers.blackmarket._customizing_weapon_data.slot - if not weapon_category or not weapon_slot then - Print("[Error] Could not load weapon customization, could not find category or slot") - end - local weapon = managers.blackmarket._global.crafted_items[weapon_category][weapon_slot] - - if not weapon then - Print("[Error] Could not load weapon customization, no weapon found in category '", weapon_category, "' slot '", weapon_slot, "'") - return - end - - -- Create default blueprint if it doesn't exist - if not weapon.visual_blueprint then - weapon.visual_blueprint = {} - local weapon = managers.blackmarket._global.crafted_items[category][slot] - for k, v in pairs( weapon.blueprint ) do - weapon.visual_blueprint[v] = clone( WeaponCustomization._default_part_visual_blueprint ) - end - end - - -- Load and apply blueprint - WeaponCustomization:LoadWeaponCustomizationFromBlueprint( weapon.visual_blueprint ) - -end - -function WeaponCustomization:LoadWeaponCustomizationFromBlueprint( blueprint, unit_override ) - - if not blueprint then - blueprint = clone( WeaponCustomization._default_part_visual_blueprint ) - Print("[Warning] Could not load weapon customization, no visual blueprint specified") - return - end - - for k, v in pairs( blueprint ) do - - local material = v["materials"] - local pattern = v["textures"] - local blackmarket_color = v["colors"] - local color = tweak_data.blackmarket.colors[ blackmarket_color ] - - self:UpdateWeapon( material, pattern, color.colors[1], color.colors[2], { [k] = true }, unit_override ) - - end - -end - -function WeaponCustomization:LoadEquippedWeaponCustomizations( weapon_base ) - - local equipped_weapons = { - [1] = managers.blackmarket:equipped_primary(), - [2] = managers.blackmarket:equipped_secondary() - } - - for k, v in pairs( equipped_weapons ) do - if weapon_base._factory_id == v.factory_id and v.visual_blueprint then - WeaponCustomization:LoadWeaponCustomizationFromBlueprint( v.visual_blueprint, weapon_base._unit ) - end - end - -end - --- Clear Data -function WeaponCustomization:ShowClearDataConfirmation() - - local title = managers.localization:text("WeaponCustomization_ClearDataTitle") - local message = managers.localization:text("WeaponCustomization_ClearDataMessage") - local menuOptions = {} - menuOptions[1] = { - text = managers.localization:text("WeaponCustomization_ClearDataAccept"), - callback = WeaponCustomization.ClearDataFromSave, - is_cancel_button = true - } - menuOptions[2] = { - text = managers.localization:text("WeaponCustomization_ClearDataCancel"), - is_cancel_button = true - } - local menu = SimpleMenu:New(title, message, menuOptions) - menu:Show() - -end - -function WeaponCustomization.ClearDataFromSave() - - if not managers.blackmarket then - return - end - - -- Erase primary weapons - for k, v in pairs( managers.blackmarket._global.crafted_items["primaries"] ) do - if v.visual_blueprint then - v.visual_blueprint = nil - end - end - - -- Erase secondary weapons - for k, v in pairs( managers.blackmarket._global.crafted_items["secondaries"] ) do - if v.visual_blueprint then - v.visual_blueprint = nil - end - end - -end --- END OF FILE diff --git a/GoonBase/mods/weapon_customization_menus.lua b/GoonBase/mods/weapon_customization_menus.lua deleted file mode 100644 index 9ee44a3..0000000 --- a/GoonBase/mods/weapon_customization_menus.lua +++ /dev/null @@ -1,795 +0,0 @@ ----------- --- Payday 2 GoonMod, Public Release Beta 2, built on 1/10/2015 2:53:10 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -local WeaponCustomization = GoonBase.WeaponCustomization -if not GoonBase.WeaponCustomization then - return -end - -local Localization = GoonBase.Localization -Localization.WeaponCustomization_MenuItem = "Customize Weapon" -Localization.bm_menu_customize_weapon_title = "Customize Weapon: $weapon_name" - -Localization.wc_modifying_parts = "Modifying Parts" -Localization.wc_not_modifying_parts = "Not Modifying Parts" -Localization.wc_highlighted_mod = "Highlighted" -Localization.wc_unavailable_mod = "Unavailable" -Localization.wc_advanced_options = "Advanced" - -Localization.wc_advanced_options_menu = "Advanced Options" - -Localization.wc_adv_clear_weapon = "Revert to Factory Standard" -Localization.wc_adv_toggle_preview_spin = "Toggle Rotating Preview" -Localization.wc_adv_toggle_colour_grading = "Toggle Ingame Colour Grading" - -Localization.wc_clear_weapon_title = "Revert Weapon" -Localization.wc_clear_weapon_message = "This will revert your weapon customization back to factory standard, do you wish to continue?" -Localization.wc_clear_weapon_accept = "Revert" -Localization.wc_clear_weapon_cancel = "Cancel" - -Localization.wc_mod_overrides_not_installed_title = "Mod Overrides Missing" -Localization.wc_mod_overrides_not_installed_desc = [[The mod overrides required to make the Weapon Customization mod are missing from your game mod_overrides folder. -You must install these, or else you will not see your weapon customization. - -A link to the download is found below. Extract the inner folder 'GoonModWeaponCustomizer' to your mod_overrides folder in your Payday 2 assets folder, and then restart your game.]] -Localization.wc_mod_overrides_not_installed_download = "Download Now" -Localization.wc_mod_overrides_not_installed_dont_show = "I Know, Don't Show This Again" -Localization.wc_mod_overrides_not_installed_cancel = "Later" - -WeaponCustomization._advanced_menu_options = { - [1] = { - text = "wc_adv_toggle_preview_spin", - func = "AdvancedToggleWeaponSpin", - }, - [2] = { - text = "wc_adv_toggle_colour_grading", - func = "AddvancedToggleColourGrading", - }, - [3] = { - text = "wc_adv_clear_weapon", - func = "AdvancedClearWeaponCheck", - } -} -WeaponCustomization._menu_text_scaling = 0.85 - -WeaponCustomization._controller_index = { - modifying = 1, - not_modifying = 1, -} - -local BTN_X = utf8.char(57346) -local BTN_Y = utf8.char(57347) -local BTN_LT = utf8.char(57354) -local BTN_LB = utf8.char(57352) -local BTN_RT = utf8.char(57355) -local BTN_RB = utf8.char(57353) -local BTN_START = utf8.char(57349) - -function WeaponCustomization:IsUsingController() - local type = managers.controller:get_default_wrapper_type() - return type == "xbox360" or type == "ps3" -end - -function WeaponCustomization.weapon_visual_customization_callback(self, data) - - local all_mods_by_type = { - materials = managers.blackmarket:get_inventory_category("materials"), - textures = managers.blackmarket:get_inventory_category("textures"), - colors = managers.blackmarket:get_inventory_category("colors") - } - - local new_node_data = {} - local list = { - "materials", - "textures", - "colors" - } - - local mask_default_blueprint = { - ["materials"] = { - id = "plastic", - global_value = "normal", - }, - ["textures"] = { - id = "no_color_no_material", - global_value = "normal", - }, - ["colors"] = { - id = "nothing", - global_value = "normal", - }, - } - - for _, category in pairs(list) do - - local listed_items = {} - local items = all_mods_by_type[category] - local default = mask_default_blueprint[category] - local mods = {} - - if default then - if default.id == "no_color_no_material" then - default = managers.blackmarket:customize_mask_category_default(category) - end - if default.id ~= "nothing" and default.id ~= "no_color_no_material" then - table.insert(mods, default) - mods[#mods].pcs = {0} - mods[#mods].default = true - listed_items[default.id] = true - end - end - - if category == "materials" and not listed_items.plastic then - table.insert(mods, {id = "plastic", global_value = "normal"}) - mods[#mods].pcs = {0} - mods[#mods].default = true - end - - local td - for i = 1, #items do - td = tweak_data.blackmarket[category][items[i].id] - if not listed_items[items[i].id] and td.texture or td.colors then - table.insert(mods, items[i]) - mods[#mods].pc = td.pc or td.pcs and td.pcs[1] or 10 - mods[#mods].colors = td.colors - end - end - - local sort_td = tweak_data.blackmarket[category] - local x_pc, y_pc - table.sort(mods, function(x, y) - if x.colors and y.colors then - for i = 1, 2 do - local x_color = x.colors[i] - local x_max = math.max(x_color.r, x_color.g, x_color.b) - local x_min = math.min(x_color.r, x_color.g, x_color.b) - local x_diff = x_max - x_min - local x_wl - if x_max == x_min then - x_wl = 10 - x_color.r - elseif x_max == x_color.r then - x_wl = (x_color.g - x_color.b) / x_diff % 6 - elseif x_max == x_color.g then - x_wl = (x_color.b - x_color.r) / x_diff + 2 - elseif x_max == x_color.b then - x_wl = (x_color.r - x_color.g) / x_diff + 4 - end - local y_color = y.colors[i] - local y_max = math.max(y_color.r, y_color.g, y_color.b) - local y_min = math.min(y_color.r, y_color.g, y_color.b) - local y_diff = y_max - y_min - local y_wl - if y_max == y_min then - y_wl = 10 - y_color.r - elseif y_max == y_color.r then - y_wl = (y_color.g - y_color.b) / y_diff % 6 - elseif y_max == y_color.g then - y_wl = (y_color.b - y_color.r) / y_diff + 2 - elseif y_max == y_color.b then - y_wl = (y_color.r - y_color.g) / y_diff + 4 - end - if x_wl ~= y_wl then - return x_wl < y_wl - end - end - end - x_pc = x.pc or x.pcs and x.pcs[1] or 1001 - y_pc = y.pc or y.pcs and y.pcs[1] or 1001 - x_pc = x_pc + (x.global_value and tweak_data.lootdrop.global_values[x.global_value].sort_number or 0) - y_pc = y_pc + (y.global_value and tweak_data.lootdrop.global_values[y.global_value].sort_number or 0) - return x_pc < y_pc - end) - - local max_x = 6 - local max_y = 3 - local mod_data = mods or {} - table.insert(new_node_data, { - name = category, - category = category, - prev_node_data = data, - name_localized = managers.localization:to_upper_text("bm_menu_" .. category), - on_create_func_name = "populate_choose_mask_mod", - on_create_data = mod_data, - override_slots = {max_x, max_y}, - identifier = BlackMarketGui.identifiers.weapon_customization - }) - - end - - new_node_data.topic_id = "bm_menu_customize_weapon_title" - new_node_data.topic_params = { - weapon_name = data.name_localized - } - - local params = {} - params.yes_func = callback(self, self, "_dialog_yes", callback(self, self, "_abort_customized_mask_callback")) - params.no_func = callback(self, self, "_dialog_no") - - new_node_data.back_callback = callback(self, self, "_warn_abort_customized_mask_callback", params) - new_node_data.blur_fade = self._data.blur_fade - new_node_data.weapon_slot_data = data - - if data.category == "primaries" or data.category == "secondaries" then - managers.blackmarket:view_weapon( data.category, data.slot, callback(self, self, "_open_weapon_customization_preview_node", {new_node_data}) ) - end - if data.category == "melee_weapons" then - -- Melee weapons don't work properly yet, so don't uncomment this unless you want to fix it yourself - -- managers.menu:open_node(self._preview_node_name, {}) - -- managers.blackmarket:preview_melee_weapon(data.name) - -- self:_open_weapon_customization_preview_node( {new_node_data} ) - end - -end - -function WeaponCustomization._open_weapon_customization_preview_node(self, data) - - managers.blackmarket._customizing_weapon = true - managers.blackmarket._customizing_weapon_data = data[1].weapon_slot_data - - local category = data[1].weapon_slot_data.category - local slot = data[1].weapon_slot_data.slot - local weapon = managers.blackmarket._global.crafted_items[category][slot] - - WeaponCustomization:CreateCustomizablePartsList( weapon ) - managers.blackmarket._selected_weapon_parts = clone( WeaponCustomization._default_part_visual_blueprint ) - - WeaponCustomization._controller_index = { - modifying = 1, - not_modifying = 1, - } - - managers.menu:open_node("blackmarket_mask_node", data) - WeaponCustomization:LoadCurrentWeaponCustomization( category, slot ) - - WeaponCustomization:Temp_CheckOverridesInstalled() - -end - -Hooks:Add("MenuSceneManagerOverrideSceneTemplate", "MenuSceneManagerOverrideSceneTemplate_WeaponCustomization", function(scene, template, data, custom_name, skip_transition) - if managers.blackmarket._customizing_weapon and template == "blackmarket_mask" then - return "blackmarket_item" - end -end) - -Hooks:Add("BlackMarketGUIStartPageData", "BlackMarketGUIStartPageData_WeaponCustomization", function(gui) - if gui.identifiers then - gui.identifiers.weapon_customization = Idstring("weapon_customization") - end -end) - -Hooks:Add("BlackMarketGUIPostSetup", "BlackMarketGUIPostSetup_WeaponCustomization", function(gui, is_start_page, component_data) - - gui.customize_weapon_visuals = function(gui, data) - WeaponCustomization.weapon_visual_customization_callback(gui, data) - end - gui._open_weapon_customization_preview_node = function(gui, data) - WeaponCustomization._open_weapon_customization_preview_node(gui, data) - end - - local w_visual_customize = { - prio = 5, - btn = "BTN_BACK", - pc_btn = Idstring("toggle_chat"), - name = "WeaponCustomization_MenuItem", - callback = callback(gui, gui, "customize_weapon_visuals") - } - - local btn_x = 10 - gui._btns["w_visual_customize"] = BlackMarketGuiButtonItem:new(gui._buttons, w_visual_customize, btn_x) - -end) - -Hooks:Add("BlackMarketGUIOnPopulateWeaponActionList", "BlackMarketGUIOnPopulateWeaponActionList_WeaponCustomization", function(gui, data) - if data.unlocked then - table.insert(data, "w_visual_customize") - end -end) - --- Hooks:Add("BlackMarketGUIOnPopulateMeleeWeaponActionList", "BlackMarketGUIOnPopulateMeleeWeaponActionList_WeaponCustomization", function(gui, data) --- if data.unlocked then --- table.insert(data, "w_visual_customize") --- end --- end) - -Hooks:Add("BlackMarketGUIOnPopulateWeapons", "BlackMarketGUIOnPopulateWeapons_WeaponCustomization", function(gui, category, data) - if managers.blackmarket._customizing_weapon then - managers.blackmarket._customizing_weapon = nil - end -end) - -Hooks:Add("BlackMarketGUIChooseMaskPartCallback", "BlackMarketGUIChooseMaskPartCallback_WeaponCustomization", function(gui, data) - WeaponCustomization:UpdateWeaponPartsWithMaskMod( data ) -end) - -Hooks:Add("BlackMarketGUIUpdateInfoText", "BlackMarketGUIUpdateInfoText_WeaponCustomization", function(self) - - local slot_data = self._slot_data - local tab_data = self._tabs[self._selected]._data - local prev_data = tab_data.prev_node_data - local ids_category = Idstring(slot_data.category) - local identifier = tab_data.identifier - local updated_texts = { - {text = ""}, - {text = ""}, - {text = ""}, - {text = ""}, - {text = ""}, - } - - local id = "none" - for k, v in pairs( self.identifiers ) do - if v == identifier then - id = k - end - end - - if identifier == self.identifiers.weapon_customization then - - local blackmarket = managers.blackmarket - if blackmarket._customizing_weapon and blackmarket._customizing_weapon_data and blackmarket._customizing_weapon_parts then - - local weapon_data = managers.blackmarket._customizing_weapon_data - local category = weapon_data.category - local slot = weapon_data.slot - local weapon = managers.blackmarket._global.crafted_items[category][slot] - - updated_texts[2].text = managers.localization:to_upper_text("wc_modifying_parts") .. "\n" - updated_texts[3].text = managers.localization:to_upper_text("wc_not_modifying_parts") .. "\n" - - if WeaponCustomization:IsUsingController() then - updated_texts[2].text = BTN_LT .. " " .. updated_texts[2].text - updated_texts[3].text = BTN_RT .. " " .. updated_texts[3].text - end - - local num_modifying = 0 - local num_not_modifying = 0 - for k, v in pairs( blackmarket._customizing_weapon_parts ) do - if v and v.modifying then - num_modifying = num_modifying + 1 - else - num_not_modifying = num_not_modifying + 1 - end - end - - local modifying_lines = 0 - local not_modifying_lines = 0 - for k, v in pairs( blackmarket._customizing_weapon_parts ) do - - if v.id then - local part = tweak_data.weapon.factory.parts[v.id] - if part then - - local part_name = WeaponCustomization:_GetLocalizedPartName(v.id, part) - local modifying = (v and v.modifying or false) - local part_index = modifying and 2 or 3 - - if WeaponCustomization:IsUsingController() then - - if v and v.modifying then - modifying_lines = modifying_lines + 1 - else - not_modifying_lines = not_modifying_lines + 1 - end - - -- Clamp values - if WeaponCustomization._controller_index.modifying > num_modifying then - WeaponCustomization._controller_index.modifying = 1 - end - if WeaponCustomization._controller_index.not_modifying > num_not_modifying then - WeaponCustomization._controller_index.not_modifying = 1 - end - - -- Place markers on appropriate lines - local ind = WeaponCustomization:_GetIndexFromLine(modifying and modifying_lines or not_modifying_lines, modifying) - if modifying then - if WeaponCustomization._controller_index.modifying == modifying_lines then - part_name = BTN_X .. " " .. part_name - end - else - if WeaponCustomization._controller_index.not_modifying == not_modifying_lines then - part_name = BTN_Y .. " " .. part_name - end - end - - end - part_name = " " .. part_name .. "\n" - - updated_texts[ part_index ].text = updated_texts[ part_index ].text .. part_name - - end - end - - end - - end - - -- Add advanced options - self._info_texts_color[5] = tweak_data.screen_colors.text - updated_texts[5].text = "\n" - if WeaponCustomization:IsUsingController() then - updated_texts[5].text = updated_texts[5].text .. BTN_START .. " " - end - updated_texts[5].text = updated_texts[5].text .. managers.localization:to_upper_text("wc_advanced_options") .. "\n" - for k, v in ipairs( WeaponCustomization._advanced_menu_options ) do - updated_texts[5].text = updated_texts[5].text .. managers.localization:text( v.text ) .. "\n" - end - - -- Selected Mod - updated_texts[4].text = "\n" - if slot_data then - - updated_texts[4].text = "\n" .. managers.localization:to_upper_text("wc_highlighted_mod") .. "\n" .. slot_data.name_localized - - if not slot_data.unlocked or (type(slot_data.unlocked) == "number" and slot_data.unlocked <= 0) then - self._info_texts_color[5] = tweak_data.screen_colors.important_1 - updated_texts[5].text = managers.localization:text("wc_unavailable_mod") - end - - end - - -- updated_texts[5].text = "" - -- updated_texts[5].text = updated_texts[5].text .. "\n BTN_X: " .. BTN_X - -- updated_texts[5].text = updated_texts[5].text .. "\n BTN_Y: " .. BTN_Y - -- updated_texts[5].text = updated_texts[5].text .. "\n BTN_LB: " .. BTN_LB - -- updated_texts[5].text = updated_texts[5].text .. "\n BTN_LT: " .. BTN_LT - -- updated_texts[5].text = updated_texts[5].text .. "\n BTN_RB: " .. BTN_RB - -- updated_texts[5].text = updated_texts[5].text .. "\n BTN_RT: " .. BTN_RT - - -- Update texts - self:_update_info_text(slot_data, updated_texts, nil, WeaponCustomization._menu_text_scaling) - - end - -end) - -Hooks:Add("BlackMarketGUIMouseReleased", "BlackMarketGUIMouseReleased_WeaponCustomization", function(gui, button, x, y) - - if not managers.blackmarket._customizing_weapon then - return - end - - if button == Idstring("0") then - WeaponCustomization:LeftMouseReleased(gui, button, x, y) - end - - if button == Idstring("1") then - WeaponCustomization:RightMouseReleased(gui, button, x, y) - end - - -end) - -Hooks:Add("MenuUpdate", "MenuUpdate_WeaponCustomization", function(t, dt) - - if WeaponCustomization:IsUsingController() and managers.blackmarket._customizing_weapon then - - local controller = managers.menu:get_controller() - local r_trigger = controller:get_input_pressed("primary_attack") - local l_trigger = controller:get_input_pressed("secondary_attack") - local button_x = controller:get_input_pressed("reload") - local button_y = controller:get_input_pressed("switch_weapon") - local start = controller:get_input_pressed("start") - - -- Cycle through modifying and not modifying options - if r_trigger then - WeaponCustomization._controller_index.not_modifying = WeaponCustomization._controller_index.not_modifying + 1 - WeaponCustomization:_UpdateBlackmarketGUI() - end - - if l_trigger then - WeaponCustomization._controller_index.modifying = WeaponCustomization._controller_index.modifying + 1 - WeaponCustomization:_UpdateBlackmarketGUI() - end - - -- Switch modifying and not modifying items - if button_x then - WeaponCustomization:_SwapWeaponPartModifyingStatus(WeaponCustomization._controller_index.modifying, true, true) - end - - if button_y then - WeaponCustomization:_SwapWeaponPartModifyingStatus(WeaponCustomization._controller_index.not_modifying, false, true) - end - - -- Controller advanced options - if start then - WeaponCustomization:ShowControllerAdvancedOptions() - end - - end - -end) - -function WeaponCustomization:_UpdateBlackmarketGUI() - if managers.menu_component and managers.menu_component._blackmarket_gui then - managers.menu_component._blackmarket_gui:update_info_text() - end -end - -function WeaponCustomization:LeftMouseReleased(gui, button, x, y) - WeaponCustomization:LeftMouseReleased_SelectParts(gui, button, x, y) - WeaponCustomization:LeftMouseReleased_Advanced(gui, button, x, y) -end - -function WeaponCustomization:LeftMouseReleased_SelectParts(gui, button, x, y) - - for k, v in pairs( gui._info_texts ) do - if v:inside(x, y) then - - local line = WeaponCustomization:_GetLineFromObjectRect(x, y, v) - 1 - local modifying_item = nil - if k == 2 then modifying_item = true end - if k == 3 then modifying_item = false end - - if modifying_item ~= nil then - WeaponCustomization:_SwapWeaponPartModifyingStatus(line, modifying_item) - gui:update_info_text() - break - end - - end - end - -end - -function WeaponCustomization:LeftMouseReleased_Advanced(gui, button, x, y) - - local v = gui._info_texts[5] - if not v then - return - end - - if v:inside(x, y) then - - local line = WeaponCustomization:_GetLineFromObjectRect(x, y, v) - 2 - local adv_option = self._advanced_menu_options[ line ] - - if adv_option and adv_option.func then - self[ adv_option.func ]() - end - - end - -end - -function WeaponCustomization:RightMouseReleased(gui, button, x, y) - - for k, v in pairs( gui._info_texts ) do - if v:inside(x, y) then - - local line = WeaponCustomization:_GetLineFromObjectRect(x, y, v) - 1 - local modifying_item = nil - if k == 2 then modifying_item = true end - if k == 3 then modifying_item = false end - - if modifying_item ~= nil then - - local tbl_index = WeaponCustomization:_GetIndexFromLine(line, modifying_item) - - if tbl_index and managers.blackmarket._customizing_weapon_parts[ tbl_index ] then - local mod = managers.blackmarket._customizing_weapon_parts[ tbl_index ].modifying - for a, b in ipairs( managers.blackmarket._customizing_weapon_parts ) do - if a ~= tbl_index then - managers.blackmarket._customizing_weapon_parts[a].modifying = not mod - end - end - end - - gui:update_info_text() - break - - end - - end - end - -end - -function WeaponCustomization:_SwapWeaponPartModifyingStatus( line, modifying, update ) - local tbl_index = WeaponCustomization:_GetIndexFromLine(line, modifying) - if tbl_index and managers.blackmarket._customizing_weapon_parts[ tbl_index ] then - managers.blackmarket._customizing_weapon_parts[ tbl_index ].modifying = not managers.blackmarket._customizing_weapon_parts[ tbl_index ].modifying - end - if update then - WeaponCustomization:_UpdateBlackmarketGUI() - end -end - -function WeaponCustomization:_IsInObjectRect(x, y, obj) - local rx, ry, rw, rh = obj:text_rect() - if x >= rx and x <= rx + rw and y >= ry and y <= ry + rh then - return true - end - return false -end - -function WeaponCustomization:_GetLineFromObjectRect(x, y, obj) - local rx, ry, rw, rh = obj:text_rect() - local font_size = tweak_data.menu.pd2_small_font_size * WeaponCustomization._menu_text_scaling - y = y - ry + font_size / 2 - y = y / (font_size * 1.05) - return math.round_with_precision(y, 0) -end - -function WeaponCustomization:_GetIndexFromLine(line, modifying) - - local i = 0 - - for k, v in ipairs( managers.blackmarket._customizing_weapon_parts ) do - if (modifying and v.modifying) or (not modifying and not v.modifying) then - i = i + 1 - if i == line then - return k - end - end - end - - return nil - -end - --- Clear Weapon Function -function WeaponCustomization:AdvancedToggleWeaponSpin() - - if managers.menu_scene then - if managers.menu_scene._disable_rotate ~= nil then - managers.menu_scene._disable_rotate = not managers.menu_scene._disable_rotate - else - managers.menu_scene._disable_rotate = true - end - end - -end - -function WeaponCustomization:AddvancedToggleColourGrading() - - if managers and managers.environment_controller then - - local self = WeaponCustomization - local grading = "color_payday" - if self._previous_colour_grading then - grading = self._previous_colour_grading - self._previous_colour_grading = nil - else - self._previous_colour_grading = managers.environment_controller:default_color_grading() - end - - managers.environment_controller:set_default_color_grading( grading ) - managers.environment_controller:refresh_render_settings() - - end - -end - -function WeaponCustomization:AdvancedClearWeaponCheck() - - local title = managers.localization:text("wc_clear_weapon_title") - local message = managers.localization:text("wc_clear_weapon_message") - local menuOptions = {} - menuOptions[1] = { - text = managers.localization:text("wc_clear_weapon_accept"), - callback = WeaponCustomization.AdvancedClearWeaponAccept, - is_cancel_button = true - } - menuOptions[2] = { - text = managers.localization:text("wc_clear_weapon_cancel"), - is_cancel_button = true - } - local menu = SimpleMenu:New(title, message, menuOptions) - menu:Show() - -end - -function WeaponCustomization.AdvancedClearWeaponAccept() - - if managers.blackmarket._customizing_weapon_data then - - local category = managers.blackmarket._customizing_weapon_data.category - local slot = managers.blackmarket._customizing_weapon_data.slot - local weapon = managers.blackmarket._global.crafted_items[category][slot] - - -- Rebuild weapon parts list - WeaponCustomization:CreateCustomizablePartsList( weapon ) - - -- Clear selected parts - managers.blackmarket._selected_weapon_parts = clone( WeaponCustomization._default_part_visual_blueprint ) - - -- Save - WeaponCustomization:UpdateWeaponPartsWithMod( nil, nil, managers.blackmarket._customizing_weapon_parts, true ) - - -- Clear data on slot - if weapon.visual_blueprint then - weapon.visual_blueprint = nil - end - - end - -end - --- Advanced options for controller -function WeaponCustomization:ShowControllerAdvancedOptions() - - local title = managers.localization:text("wc_advanced_options_menu") - local message = "" - local menuOptions = {} - - local i = 1 - for k, v in ipairs( WeaponCustomization._advanced_menu_options ) do - menuOptions[i] = { - text = managers.localization:text( v.text ), - callback = WeaponCustomization[v.func], - is_cancel_button = true - } - i = i + 1 - end - - menuOptions[i] = { - text = managers.localization:text("wc_clear_weapon_cancel"), - is_cancel_button = true - } - - local menu = SimpleMenu:New(title, message, menuOptions) - menu:Show() - -end - --- Temporary Popup -function WeaponCustomization:Temp_CheckOverridesInstalled() - - local file_name = "assets/mod_overrides/GoonModWeaponCustomizer/units/payday2/weapons/wpn_fps_ass_74_pts/wpn_fps_ass_74_b_standard.material_config" - file_name = Application:base_path() .. file_name - - local f= io.open(file_name, "r") - if f ~= nil then - io.close(f) - else - if GoonBase.Options.WeaponCustomization and not GoonBase.Options.WeaponCustomization.TempShownOverridesNotInstalled then - WeaponCustomization:Temp_ShowOverridesNotInstalledWindow() - end - end - -end - -function WeaponCustomization:Temp_ShowOverridesNotInstalledWindow() - - local title = managers.localization:text("wc_mod_overrides_not_installed_title") - local message = managers.localization:text("wc_mod_overrides_not_installed_desc") - local menuOptions = {} - menuOptions[1] = { - text = managers.localization:text("wc_mod_overrides_not_installed_download"), - callback = WeaponCustomization.Temp_DownloadOverrides, - is_cancel_button = true - } - menuOptions[2] = { - text = managers.localization:text("wc_mod_overrides_not_installed_dont_show"), - callback = WeaponCustomization.Temp_DontShowInFuture, - is_cancel_button = true - } - menuOptions[3] = { - text = managers.localization:text("wc_mod_overrides_not_installed_cancel"), - is_cancel_button = true - } - local menu = SimpleMenu:New(title, message, menuOptions) - menu:Show() - -end - -function WeaponCustomization:Temp_DownloadOverrides() - - if SystemInfo:platform() == Idstring("WIN32") then - os.execute( "explorer " .. WeaponCustomization._mod_overrides_download_location ) - os.execute( "explorer " .. Application:base_path() .. "assets\\mod_overrides\\" ) - end - -end - -function WeaponCustomization:Temp_DontShowInFuture() - - if GoonBase.Options.WeaponCustomization then - GoonBase.Options.WeaponCustomization.TempShownOverridesNotInstalled = true - GoonBase.Options:Save() - end - -end --- END OF FILE diff --git a/GoonBase/mods/weapon_customization_part_data.lua b/GoonBase/mods/weapon_customization_part_data.lua deleted file mode 100644 index 0c66c5d..0000000 --- a/GoonBase/mods/weapon_customization_part_data.lua +++ /dev/null @@ -1,372 +0,0 @@ ----------- --- Payday 2 GoonMod, Public Release Beta 2, built on 1/23/2015 10:01:12 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -local WeaponCustomization = GoonBase.WeaponCustomization -if not GoonBase.WeaponCustomization then - return -end - -function WeaponCustomization:_GetLocalizedPartName( part_name, part ) - return string.upper( self:GetLocalizedPartName( part_name, part ) ) -end - -function WeaponCustomization:GetLocalizedPartName( part_name, part ) - if part == "nil" then - return "No Name" - end - if WeaponCustomization.MissingPartLocalizations[ part_name ] then - return WeaponCustomization.MissingPartLocalizations[ part_name ] - end - if WeaponCustomization.MissingPartLocalizations[ part.name_id ] then - return WeaponCustomization.MissingPartLocalizations[ part.name_id ] - end - return (part.name_id and managers.localization:text( part.name_id ) or part_name) -end - -local standard_barrel = "Standard Barrel" -local standard_magazine = "Standard Magazine" -local standard_grip = "Standard Grip" -local standard_foregrip = "Standard Foregrip" -local standard_body = "Standard Body" -local standard_stock = "Standard Stock" -local standard_slide = "Standard Slide" -local standard_rail = "Standard Rail" -local standard_sights = "Standard Sights" -local standard_compensator = "Standard Compensator" -local upper_receiver = "Upper Receiver" -local lower_receiver = "Lower Receiver" -local vertical_grip = "Vertical Grip" -local gadget_adapter = "Gadget Adapter" -local optic_adapter = "Optic Adapter" -local stock_adapter = "Stock Adapter" - -WeaponCustomization.MissingPartLocalizations = { - - -- Secondaries - ["wpn_fps_pis_usp_body_standard"] = standard_body, - ["wpn_fps_pis_usp_m_standard"] = standard_magazine, - ["wpn_fps_pis_usp_fl_adapter"] = standard_body, - - ["wpn_fps_pis_g17_body_standard"] = standard_body, - ["wpn_fps_pis_g17_b_standard"] = standard_slide, - ["wpn_fps_pis_g17_m_standard"] = standard_magazine, - - ["wpn_fps_pis_beretta_b_std"] = standard_barrel, - ["wpn_fps_pis_beretta_body_beretta"] = standard_body, - ["wpn_fps_pis_beretta_m_std"] = standard_magazine, - ["wpn_fps_pis_beretta_g_std"] = standard_grip, - ["wpn_fps_pis_beretta_sl_std"] = standard_slide, - ["wpn_fps_pis_beretta_o_std"] = standard_sights, - ["bm_wp_beretta_body_rail"] = standard_rail, - - ["bm_wp_deagle_body_standard"] = standard_body, - ["bm_wp_deagle_b_standard"] = standard_barrel, - ["bm_wp_deagle_g_standard"] = standard_grip, - ["bm_wp_deagle_m_standard"] = standard_magazine, - ["bm_wp_deagle_o_standard_front"] = "Front Sight", - ["bm_wp_deagle_o_standard_front_long"] = "Front Sight", - ["bm_wp_deagle_o_standard_rear"] = "Rear Sight", - - ["bm_wp_c96_body_standard"] = standard_body, - ["bm_wp_c96_b_standard"] = standard_barrel, - ["bm_wp_c96_m_standard"] = standard_magazine, - ["bm_wp_c96_g_standard"] = standard_grip, - - ["bm_wp_g18c_b_standard"] = standard_barrel, - ["bm_wp_g18c_body_frame"] = standard_body, - - ["wpn_fps_pis_g26_body_stardard"] = standard_body, - ["wpn_fps_pis_g26_m_standard"] = standard_magazine, - ["wpn_fps_pis_g26_b_standard"] = standard_barrel, - - ["bm_wp_g22c_body_standard"] = standard_body, - ["bm_wp_g22c_b_standard"] = standard_barrel, - - ["wpn_fps_pis_judge_b_standard"] = standard_barrel, - ["wpn_fps_pis_judge_fl_adapter"] = gadget_adapter, - ["wpn_fps_pis_judge_g_standard"] = standard_grip, - ["wpn_fps_pis_judge_body_standard"] = standard_body, - - ["wpn_fps_upg_vg_ass_smg_verticalgrip"] = vertical_grip, - ["wpn_fps_upg_vg_ass_smg_verticalgrip_vanilla"] = vertical_grip, - - ["bm_wp_akmsu_fg_standard"] = standard_foregrip, - ["bm_wp_akmsu_b_standard"] = standard_barrel, - ["bm_wp_akmsu_body_lowerreceiver"] = lower_receiver, - - ["wpn_fps_smg_thompson_barrel"] = standard_barrel, - ["wpn_fps_smg_thompson_body"] = standard_body, - ["wpn_fps_smg_thompson_drummag"] = "Drum Magazine", - ["wpn_fps_smg_thompson_fl_adapter"] = gadget_adapter, - ["wpn_fps_smg_thompson_foregrip"] = standard_foregrip, - ["wpn_fps_smg_thompson_grip"] = standard_grip, - ["wpn_fps_smg_thompson_ns_standard"] = standard_compensator, - ["wpn_fps_smg_thompson_o_adapter"] = optic_adapter, - ["wpn_fps_smg_thompson_stock"] = standard_stock, - - ["wpn_fps_smg_uzi_b_standard"] = standard_barrel, - ["wpn_fps_smg_uzi_body_standard"] = standard_body, - ["wpn_fps_smg_uzi_fg_standard"] = standard_foregrip, - ["wpn_fps_smg_uzi_g_standard"] = standard_grip, - ["wpn_fps_smg_uzi_m_standard"] = standard_magazine, - ["wpn_fps_smg_uzi_s_unfolded"] = "Unfolded Stock", - - ["bm_wp_mac10_b_dummy"] = "Dummy Body", - ["bm_wp_mac10_body_mac10"] = standard_body, - - ["wpn_fps_smg_sterling_body_standard"] = standard_body, - ["wpn_fps_smg_sterling_o_adapter"] = optic_adapter, - ["wpn_fps_smg_sterling_s_standard"] = standard_stock, - ["wpn_fps_smg_sterling_m_medium"] = standard_magazine, - ["bm_wp_sterling_b_standard"] = standard_barrel, - - ["bm_wp_mp5_body_mp5"] = standard_body, - ["bm_wp_mp5_body_rail"] = standard_rail, - ["bm_wp_mp5_m_std"] = standard_magazine, - - ["bm_wp_mp7_b_standard"] = standard_magazine, - ["bm_wp_mp7_body_standard"] = standard_body, - ["bm_wp_mp7_m_short"] = "Short Magazine", - ["bm_wp_mp7_s_standard"] = standard_stock, - - ["bm_wp_mp9_b_dummy"] = "Dummy Body", - ["bm_wp_mp9_body_mp9"] = standard_body, - - ["bm_wp_p226_b_standard"] = standard_barrel, - ["bm_wp_p226_body_standard"] = standard_body, - ["bm_wp_p226_g_standard"] = standard_grip, - ["bm_wp_p226_m_standard"] = standard_magazine, - ["bm_wp_p226_o_long"] = standard_sights, - ["bm_wp_p226_o_standard"] = standard_sights, - - ["bm_wp_p90_body_p90"] = standard_body, - ["bm_wp_p90_m_std"] = standard_magazine, - - ["bm_wp_pis_rage_o_adapter"] = optic_adapter, - ["bm_wp_rage_b_standard"] = standard_barrel, - ["bm_wp_rage_body_standard"] = standard_body, - ["bm_wp_rage_g_standard"] = standard_grip, - - ["bm_wp_scorpion_m_standard"] = standard_magazine, - ["wpn_fps_smg_scorpion_b_standard"] = standard_barrel, - ["wpn_fps_smg_scorpion_body_standard"] = standard_body, - ["wpn_fps_smg_scorpion_extra_rail"] = standard_rail, - ["wpn_fps_smg_scorpion_extra_rail_gadget"] = "Rail Gadget", - ["wpn_fps_smg_scorpion_g_standard"] = standard_grip, - ["wpn_fps_smg_scorpion_s_standard"] = standard_stock, - - ["wpn_fps_smg_tec9_b_long"] = "Long Barrel", - ["wpn_fps_smg_tec9_body_standard"] = standard_body, - ["wpn_fps_smg_tec9_m_standard"] = standard_magazine, - - ["wpn_fps_pis_ppk_b_standard"] = standard_barrel, - ["wpn_fps_pis_ppk_body_standard"] = standard_body, - ["wpn_fps_pis_ppk_fl_mount"] = gadget_adapter, - ["wpn_fps_pis_ppk_g_standard"] = standard_grip, - ["wpn_fps_pis_ppk_m_standard"] = standard_magazine, - - ["wpn_fps_smg_m45_g_standard"] = standard_grip, - ["wpn_fps_smg_m45_s_standard"] = standard_stock, - ["wpn_fps_smg_m45_m_mag"] = standard_magazine, - ["wpn_fps_smg_m45_b_standard"] = standard_barrel, - ["wpn_fps_smg_m45_body_standard"] = standard_body, - - ["bm_wp_upg_o_marksmansight_front"] = "Front Marksman Sight", - - ["bm_wp_striker_b_standard"] = standard_barrel, - ["bm_wp_striker_body_standard"] = standard_body, - - -- Primaries - ["wpn_fps_amcar_uupg_fg_amcar"] = standard_foregrip, - ["wpn_fps_amcar_uupg_body_upperreciever"] = upper_receiver, - ["bm_wp_m4_lower_reciever"] = lower_receiver, - ["bm_wp_m4_g_standard"] = standard_grip, - - ["wpn_fps_ass_fal_body_standard"] = standard_body, - ["wpn_fps_ass_fal_fg_standard"] = standard_foregrip, - ["wpn_fps_ass_fal_g_standard"] = standard_grip, - ["wpn_fps_ass_fal_m_standard"] = standard_magazine, - ["wpn_fps_ass_fal_s_standard"] = standard_stock, - - ["wpn_fps_snp_msr_body_wood"] = "Wooden Body", - ["wpn_fps_snp_msr_m_standard"] = standard_magazine, - ["wpn_fps_snp_msr_b_standard"] = standard_barrel, - - ["wpn_fps_snp_mosin_b_medium"] = "Medium Barrel", - ["wpn_fps_snp_mosin_rail"] = "Nagant Rail", - ["wpn_fps_snp_mosin_body_standard"] = standard_body, - ["wpn_fps_snp_mosin_m_standard"] = standard_magazine, - - ["wpn_fps_snp_r93_b_standard"] = standard_barrel, - ["wpn_fps_snp_r93_body_standard"] = standard_body, - ["wpn_fps_snp_r93_m_std"] = standard_magazine, - - ["wpn_fps_snp_m95_bipod"] = "Bipod", - ["wpn_fps_snp_m95_barrel_standard"] = standard_barrel, - ["wpn_fps_snp_m95_lower_reciever"] = lower_receiver, - ["wpn_fps_snp_m95_upper_reciever"] = upper_receiver, - ["wpn_fps_snp_m95_magazine"] = standard_magazine, - - ["wpn_fps_aug_body_aug"] = standard_body, - ["wpn_fps_aug_m_pmag"] = standard_magazine, - - ["bm_wp_famas_body_standard"] = standard_body, - ["bm_wp_famas_b_standard"] = standard_barrel, - ["bm_wp_famas_g_standard"] = standard_grip, - ["bm_wp_famas_m_standard"] = standard_magazine, - ["bm_wp_famas_o_extra"] = optic_adapter, - - ["wpn_fps_sho_ben_s_collapsable"] = "Collapsable Stock", - ["wpn_fps_sho_ben_b_standard"] = standard_barrel, - ["wpn_fps_sho_ben_body_standard"] = standard_body, - - ["bm_wp_r870_body_standard"] = standard_body, - ["bm_wp_r870_b_long"] = standard_barrel, - ["bm_wp_r870_fg_big"] = standard_foregrip, - ["wpn_fps_shot_r870_b_short"] = "Short Barrel", - - ["bm_wp_ak5_b_std"] = standard_barrel, - ["bm_wp_ak5_body_ak5"] = standard_body, - ["bm_wp_ak5_body_rail"] = standard_rail, - - ["bm_wp_ak_body_lowerreceiver"] = lower_receiver, - ["bm_wp_ak_g_standard"] = standard_grip, - ["bm_wp_ak_m_akm"] = standard_magazine, - ["bm_wp_ak_fg_standard"] = standard_foregrip, - - ["bm_wp_akm_body_upperreceiver"] = "AK Dustcover", - ["bm_wp_akm_b_standard"] = standard_barrel, - - ["bm_wp_74_m_standard"] = standard_magazine, - ["bm_wp_74_b_standard"] = standard_barrel, - - ["bm_wp_ak_fg_standard_gold"] = "Golden Foregrip", - ["bm_wp_ak_m_akm_gold"] = "Golden Magazine", - ["bm_wp_akm_b_standard_gold"] = "Golden Barrel", - ["bm_wp_akm_body_upperreceiver_gold"] = "Golden Upper Receiver", - ["bm_wp_ak_body_lowerreceiver_gold"] = "Golden Lower Receiver", - - ["bm_wp_gre_m79_barrel"] = "Barrel", - ["bm_wp_gre_m79_grenade"] = "Grenade", - ["bm_wp_m79_barrelcatch"] = "Barrel Catch", - ["bm_wp_m79_sight_up"] = "Sight", - - ["bm_wp_g3_b_long"] = standard_barrel, - ["bm_wp_g3_body_lower"] = lower_receiver, - ["bm_wp_g3_body_upper"] = upper_receiver, - ["bm_wp_g3_fg_standard"] = standard_foregrip, - ["bm_wp_g3_m_standard"] = standard_magazine, - - ["bm_wp_g36_body_standard"] = standard_body, - ["bm_wp_g36_body_sl8"] = "Sniper Body", - ["bm_wp_g36_g_standard"] = standard_grip, - ["bm_wp_g36_m_standard"] = standard_magazine, - ["bm_wp_g36_s_standard"] = standard_stock, - - ["bm_wp_galil_body_standard"] = standard_body, - ["bm_wp_galil_fg_standard"] = standard_foregrip, - ["bm_wp_galil_g_standard"] = standard_grip, - ["bm_wp_galil_m_standard"] = standard_magazine, - ["bm_wp_galil_s_standard"] = standard_stock, - - ["wpn_fps_lmg_hk21_b_short"] = "Short Barrel", - ["wpn_fps_lmg_hk21_body_lower"] = lower_receiver, - ["wpn_fps_lmg_hk21_body_upper"] = upper_receiver, - ["wpn_fps_lmg_hk21_fg_long"] = "Long Foregrip", - ["wpn_fps_lmg_hk21_g_standard"] = standard_grip, - ["wpn_fps_lmg_hk21_m_standard"] = standard_magazine, - ["wpn_fps_lmg_hk21_s_standard"] = standard_stock, - - ["bm_wp_huntsman_body_standard"] = standard_body, - - ["bm_wp_ksg_b_standard"] = standard_barrel, - ["bm_wp_ksg_body_standard"] = standard_body, - ["bm_wp_ksg_fg_short"] = standard_foregrip, - ["wpn_fps_sho_ksg_fg_standard"] = standard_foregrip, - ["bm_wp_upg_o_mbus_front"] = "Front Flip-up Sight", - ["bm_wp_upg_o_dd_front"] = "Front Sight", - ["bm_wp_upg_o_dd_rear"] = "Rear Sight", - - ["wpn_fps_ass_l85a2_b_medium"] = "Medium Barrel", - ["wpn_fps_ass_l85a2_body_standard"] = standard_body, - ["wpn_fps_ass_l85a2_g_standard"] = standard_grip, - ["wpn_fps_ass_l85a2_ns_standard"] = standard_compensator, - ["wpn_fps_ass_l85a2_o_standard"] = standard_sights, - ["wpn_fps_ass_l85a2_fg_medium"] = standard_foregrip, - - ["bm_wp_m14_m_standard"] = standard_magazine, - ["bm_wp_m14_b_standard"] = standard_barrel, - ["bm_wp_m14_body_lower"] = lower_receiver, - ["bm_wp_m14_body_upper"] = upper_receiver, - - ["bm_wp_m16_fg_standard"] = standard_foregrip, - ["bm_wp_m16_s_solid"] = standard_stock, - ["bm_wp_m16_os_frontsight"] = standard_sights, - - ["wpn_fps_lmg_m249_b_short"] = "Short Barrel", - ["wpn_fps_lmg_m249_body_standard"] = standard_body, - ["wpn_fps_lmg_m249_fg_standard"] = standard_foregrip, - ["wpn_fps_lmg_m249_m_standard"] = "Box Magazine", - ["wpn_fps_lmg_m249_s_modern"] = "Modern Stock", - ["wpn_fps_lmg_m249_s_para"] = standard_stock, - ["wpn_fps_lmg_m249_upper_reciever"] = upper_receiver, - - ["bm_wp_m4_g_standard"] = standard_grip, - ["bm_wp_m4_lower_reciever"] = lower_receiver, - ["bm_wp_m4_s_adapter"] = stock_adapter, - ["bm_wp_m4_uupg_draghandle"] = "Drag Handle", - ["bm_wp_m4_uupg_fg_rail_ext"] = "Foregrip Rail", - ["bm_wp_m4_upg_b_sd_smr"] = "SMR", - - ["wpn_fps_lmg_mg42_n42"] = standard_compensator, - ["wpn_fps_lmg_mg42_b_mg42"] = standard_barrel, - ["wpn_fps_lmg_mg42_reciever"] = "Receiver and Stock", - - ["wpn_fps_lmg_rpk_b_standard"] = standard_barrel, - ["wpn_fps_lmg_rpk_body_lowerreceiver"] = lower_receiver, - ["wpn_fps_lmg_rpk_fg_wood"] = "Wooden Foregrip", - ["wpn_lmg_rpk_m_drum"] = "Drum Magazine", - ["wpn_fps_lmg_rpk_s_wood"] = "Wooden Stock", - - ["bm_wp_saiga_b_standard"] = standard_barrel, - ["bm_wp_saiga_fg_standard"] = standard_foregrip, - ["bm_wp_saiga_m_5rnd"] = standard_magazine, - - ["bm_wp_saw_b_normal"] = "Blade Guard", - ["bm_wp_saw_body_standard"] = "Saw Body", - ["bm_wp_saw_m_blade"] = "Saw Blade", - - ["bm_wp_scar_b_medium"] = "Medium Barrel", - ["bm_wp_scar_body_standard"] = standard_body, - ["bm_wp_scar_m_standard"] = standard_magazine, - ["bm_wp_scar_ns_short"] = standard_compensator, - ["bm_wp_scar_ns_standard"] = standard_compensator, - ["bm_wp_scar_o_flipups_down"] = standard_sights, - ["bm_wp_scar_o_flipups_up"] = standard_sights, - ["bm_wp_scar_s_standard"] = standard_stock, - - ["wpn_fps_sho_b_spas12_long"] = "Long Barrel", - ["wpn_fps_sho_b_spas12_short"] = "Short Barrel", - ["wpn_fps_sho_body_spas12_standard"] = standard_body, - ["wpn_fps_sho_fg_spas12_standard"] = standard_foregrip, - ["wpn_fps_sho_s_spas12_unfolded"] = standard_stock, - - ["wpn_fps_ass_s552_m_standard"] = standard_magazine, - ["wpn_fps_ass_s552_o_flipup"] = standard_sights, - ["wpn_fps_ass_s552_g_standard"] = standard_grip, - ["wpn_fps_ass_s552_s_m4"] = "M4 Stock", - ["wpn_fps_ass_s552_s_standard"] = standard_stock, - ["wpn_fps_ass_s552_b_standard"] = standard_barrel, - ["wpn_fps_ass_s552_body_standard"] = standard_body, - ["wpn_fps_ass_s552_fg_standard"] = standard_foregrip, - - ["wpn_fps_shot_shorty_m_extended_short"] = "Extended Magazine", - - ["bm_wp_upg_vg_ass_smg_afg"] = "AFG", - ["bm_wp_upg_vg_ass_smg_stubby"] = "Stubby", - ["wpn_fps_upg_fl_ass_peq15_flashlight"] = "Flashlight", - -} --- END OF FILE diff --git a/GoonBase/mods/weapon_remember_gadget.lua b/GoonBase/mods/weapon_remember_gadget.lua deleted file mode 100644 index 386b954..0000000 --- a/GoonBase/mods/weapon_remember_gadget.lua +++ /dev/null @@ -1,45 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - --- Mod Definition -local Mod = class( BaseMod ) -Mod.id = "RememberGadgetState" -Mod.Name = "Remember Gadget State" -Mod.Desc = "Your weapon gadget will be in the same state as you left it when you switch weapons" -Mod.Requirements = {} -Mod.Incompatibilities = {} - -Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() - GoonBase.Mods:RegisterMod( Mod ) -end) - -if not Mod:IsEnabled() then - return -end - --- Data -_G.GoonBase.RememberGadgetState = _G.GoonBase.RememberGadgetState or {} -local Gadget = _G.GoonBase.RememberGadgetState -Gadget.States = {} - --- Hooks -Hooks:Add("PlayerStandardStartActionEquipWeapon", "PlayerStandardStartActionEquipWeapon_WeaponRememberGadget", function(ply, t) - - local weapon_base = ply._equipped_unit:base() - if weapon_base._has_gadget then - weapon_base:set_gadget_on(Gadget.States[ weapon_base._factory_id ] or 0, true) - end - -end) - -Hooks:Add("PlayerStandardChangingWeapon", "PlayerStandardChangingWeapon_" .. Mod:ID(), function(ply) - - local weapon_base = ply._equipped_unit:base() - if weapon_base._has_gadget then - Gadget.States[ weapon_base._factory_id ] = weapon_base._gadget_on or 0 - end - -end) --- END OF FILE diff --git a/GoonBase/mods/zoom_sensitivity.lua b/GoonBase/mods/zoom_sensitivity.lua deleted file mode 100644 index 20a6dc8..0000000 --- a/GoonBase/mods/zoom_sensitivity.lua +++ /dev/null @@ -1,74 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - --- Mod Definition -local Mod = class( BaseMod ) -Mod.id = "IronsightSensitivity" -Mod.Name = "Ironsight Normalized Sensitivity" -Mod.Desc = "Lower the sensitivity when using ironsights to more accurately place your shots" -Mod.Requirements = {} -Mod.Incompatibilities = {} - -Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() - GoonBase.Mods:RegisterMod( Mod ) -end) - -if not Mod:IsEnabled() then - return -end - --- Options -GoonBase.Options.IronsightSensitivity = GoonBase.Options.IronsightSensitivity or {} -GoonBase.Options.IronsightSensitivity.Enabled = true - --- Localization -local Localization = GoonBase.Localization -Localization.OptionsMenu_ZoomSensitivityTitle = "Enabled Ironsight Normalized Sensitivity" -Localization.OptionsMenu_ZoomSensitivityMessage = "Lower the sensitivity when using ironsights to more accurately place your shots" - --- Add options to menu -Hooks:Add("MenuManagerSetupGoonBaseMenu", "MenuManagerSetupGoonBaseMenu_" .. Mod:ID(), function( menu_manager ) - - local success, err = pcall(function() - - MenuCallbackHandler.toggle_zoom_sensitivity = function(this, item) - GoonBase.Options.IronsightSensitivity.Enabled = item:value() == "on" and true or false - GoonBase.Options:Save() - end - - GoonBase.MenuHelper:AddToggle({ - id = "toggle_zoom_sensitivity", - title = "OptionsMenu_ZoomSensitivityTitle", - desc = "OptionsMenu_ZoomSensitivityMessage", - callback = "toggle_zoom_sensitivity", - value = GoonBase.Options.IronsightSensitivity.Enabled, - menu_id = "goonbase_options_menu" - }) - - end) - if not success then PrintTable(err) end - -end) - -Hooks:Add( "MenuManagerSetMouseSensitivity", "MenuManagerSetMouseSensitivity_" .. Mod:ID(), function( menu_manager, zoomed ) - - if GoonBase.Options.IronsightSensitivity == nil or not GoonBase.Options.IronsightSensitivity.Enabled then - return - end - - -- Zoom sensitivity calculations by Frankelstner - local sens = managers.user:get_setting("camera_sensitivity") - if zoomed and alive( managers.player:player_unit() ) then - local currentState = managers.player:player_unit():movement():current_state() - if alive(currentState._equipped_unit) then - local fovMul = managers.user:get_setting( "fov_multiplier" ) - sens = sens * ( currentState._equipped_unit:base():zoom() or 65 ) * (fovMul + 1) / 2 / ( 65 * fovMul ) - end - end - menu_manager._controller:get_setup():get_connection("look"):set_multiplier(sens * menu_manager._look_multiplier) - managers.controller:rebind_connections() - -end ) --- END OF FILE diff --git a/GoonBase/mutators/base_mutator.lua b/GoonBase/mutators/base_mutator.lua deleted file mode 100644 index 0477793..0000000 --- a/GoonBase/mutators/base_mutator.lua +++ /dev/null @@ -1,251 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -if BaseMutator then return end -local Mutators = _G.GoonBase.Mutators - -BaseMutator = class() -BaseMutator.Id = "BaseMutator" -BaseMutator.OptionsName = "Base Mutator" -BaseMutator.OptionsDesc = "The base mutator" -BaseMutator.MenuPrefix = "toggle_mutator_" -BaseMutator.MenuSuffix = "" -BaseMutator.HideInOptionsMenu = false -BaseMutator.AllPlayersRequireMod = false -BaseMutator.Incompatibilities = {} - -BaseMutator._AllPlayersRequirementText = "\nWarning: All players must have GoonMod and Mutators installed for this mutator to work properly!" - -function BaseMutator:ID() - return self.Id -end - -function BaseMutator:IncompatibleMutators() - return self.Incompatibilities -end - -function BaseMutator:DoAllPlayersRequireMod() - return self.AllPlayersRequireMod -end - -function BaseMutator:Setup() - if self:IsEnabled() then - self:_OnEnabled() - end -end - -function BaseMutator:SetupLocalization() - - local Localization = _G.GoonBase.Localization - - self.OptionsDescOrig = self.OptionsDesc - if self:DoAllPlayersRequireMod() then - self.OptionsDesc = self.OptionsDesc .. self._AllPlayersRequirementText - end - - Localization[ self:GetName() ] = self.OptionsName - Localization[ self:GetDesc() ] = self.OptionsDesc - Localization[ self:GetOriginalDesc() ] = self.OptionsDescOrig - -end - -function BaseMutator:SetupMenu() - - -- Setup callback for mutator - local mutator_menu_name = self.MenuPrefix .. self:ID() .. self.MenuSuffix - MenuCallbackHandler[mutator_menu_name] = function(this, item) - - -- Get mutator state - local enabled = item:value() == "on" and true or false - - -- Check if we're trying to enable an incompatible mutator - if enabled then - local incompatibilities = Mutators:CheckIncompatibilities( self ) - if type(incompatibilities) == "table" then - Mutators:ShowIncompatibilitiesWindow( self, incompatibilities ) - item:set_value("off") - return - end - end - - -- Toggle mutator - self:OnToggled(enabled) - - -- Verify mutators are compatible - Mutators:VerifyAllIncompatibilities() - - end - - -- Add to menu - GoonBase.MenuHelper:AddToggle({ - id = mutator_menu_name, - title = self:GetName(), - desc = self:GetDesc(), - callback = mutator_menu_name, - value = GoonBase.Options.Mutators[ self:ID() ], - disabled_color = Color( 0.5, 1, 0.2, 0.2 ), - menu_id = Mutators.MenuID - }) - -end - -function BaseMutator:GetName() - return "mutator_" .. self.Id .. "_name" -end - -function BaseMutator:GetDesc() - return "mutator_" .. self.Id .. "_desc" -end - -function BaseMutator:GetOriginalDesc() - return "mutator_" .. self.Id .. "_desc_orig" -end - -function BaseMutator:GetLocalizedName() - return managers.localization:text( self:GetName() ) -end - -function BaseMutator:GetLocalizedDesc(ignore_requirement) - local desc = managers.localization:text( self:GetOriginalDesc() ) - if not ignore_requirement and self:DoAllPlayersRequireMod() then - desc = desc .. self._AllPlayersRequirementText - end - return desc -end - -function BaseMutator:IncompatibilitiesLocalizedDesc(incompatibilities) - - local new_str = self:GetLocalizedDesc() - - if incompatibilities ~= nil then - - local str = "" - for k, v in pairs( incompatibilities ) do - local mutator_name = Mutators.LoadedMutators[v]:GetLocalizedName() - if str ~= "" then - str = str .. ", " - end - str = str .. mutator_name - end - new_str = new_str .. " (Incompatible with " .. str .. ")" - - end - - self.OptionsDesc = new_str - GoonBase.Localization[ self:GetDesc() ] = self.OptionsDesc - -end - -function BaseMutator:VerifyIncompatibilities(skip_menu_verify) - - local should_disable = false - local incompatible_mutators = {} - - for k, v in pairs( self:IncompatibleMutators() ) do - if ( Mutators.LoadedMutators[v] ~= nil and Mutators.LoadedMutators[v]:ShouldBeEnabled() ) or Mutators.ActiveMutators[v] then - should_disable = true - table.insert( incompatible_mutators, v ) - end - end - - if not skip_menu_verify then - self:MenuSetEnabled( not should_disable ) - self:IncompatibilitiesLocalizedDesc( #incompatible_mutators > 0 and incompatible_mutators or nil ) - end - - return not should_disable - -end - -function BaseMutator:MenuSetEnabled(enabled) - - local menu = GoonBase.MenuHelper:GetMenu( Mutators.MenuID ) - for k, v in pairs( menu["_items"] ) do - local menu_name = v["_parameters"]["name"]:gsub(self.MenuPrefix, "") - if menu_name == self:ID() then - v:set_enabled( enabled ) - v:dirty() - end - end - -end - -function BaseMutator:OnToggled(enabled) - - GoonBase.Options.Mutators = GoonBase.Options.Mutators or {} - GoonBase.Options.Mutators[self.Id] = enabled - GoonBase.Options:Save() - - if enabled then - self:_OnEnabled() - else - self:_OnDisabled() - end - -end - -function BaseMutator:IsEnabled() - - if GoonBase.Network:IsMultiplayer() and not GoonBase.Network:IsHost() then - return false - end - - if Global.game_settings ~= nil then - if GoonBase.Network:IsMultiplayer() and Global.game_settings.permission == "public" then - return false - end - if Global.game_settings.active_mutators then - return Global.game_settings.active_mutators[self.Id] or false - end - end - - return false - -end - -function BaseMutator:ShouldBeEnabled() - - if GoonBase.Options.Mutators == nil then - return false - end - - return GoonBase.Options.Mutators[self.Id] or false - -end - -function BaseMutator:ForceEnable() - return self:_OnEnabled() -end - -function BaseMutator:_OnEnabled() - Mutators.ActiveMutators[self:ID()] = true - self:OnEnabled() - self:_UpdateMatchmaking() -end - -function BaseMutator:OnEnabled() - Print("Base Mutator Enabled") -end - -function BaseMutator:_OnDisabled() - if not Mutators:IsInGame() then - Mutators.ActiveMutators[self:ID()] = nil - end - self:OnDisabled() - self:_UpdateMatchmaking() -end - -function BaseMutator:OnDisabled() - Print("Base Mutator Disabled") -end - -function BaseMutator:_UpdateMatchmaking() - local psuccess, perror = pcall(function() - if MenuCallbackHandler then - MenuCallbackHandler:update_matchmake_attributes() - end - end) -end --- END OF FILE diff --git a/GoonBase/mutators/mutator_addicts.lua b/GoonBase/mutators/mutator_addicts.lua deleted file mode 100644 index a543422..0000000 --- a/GoonBase/mutators/mutator_addicts.lua +++ /dev/null @@ -1,122 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -local Mutator = class(BaseMutator) -Mutator.Id = "MethAddiction" -Mutator.OptionsName = "Addicts" -Mutator.OptionsDesc = "Players constantly lose health unless they are carrying the bag of drugs" -Mutator.AllPlayersRequireMod = true - -Hooks:Add("GoonBaseRegisterMutators", "GoonBaseRegisterMutators_" .. Mutator:ID(), function() - GoonBase.Mutators:RegisterMutator( Mutator ) -end) - -Mutator._player_lookup = {} -Mutator._player_id_lookup = {} -Mutator._drugs = { - ["coke"] = true, - ["meth"] = true, - ["coke_pure"] = true, - ["sandwich"] = true, -} -Mutator._drug_spawns = { - "coke", - "meth", - "coke_pure", -} -Mutator._addiction_restore = 0.0425 -Mutator._addiction_damage = -0.1250 - -Mutator._gameUpdateHook = "GameSetupUpdate_" .. Mutator:ID() -Mutator._playerDamageOnPostInitHook = "PlayerDamageOnPostInit_" .. Mutator:ID() - -function Mutator:IsDrugsBag( carry_data ) - if not carry_data then - return false - end - return self._drugs[carry_data.carry_id] -end - -function Mutator:ApplyAddictionDamage( unit, carrying_drugs, delta ) - - if unit and unit:character_damage() and unit:character_damage().change_health then - if carrying_drugs then - unit:character_damage():change_health( self._addiction_restore * delta ) - else - unit:character_damage():change_health( self._addiction_damage * delta ) - end - end - -end - -function Mutator:OnEnabled() - - Hooks:Add("GameSetupUpdate", self._gameUpdateHook, function(t, dt) - - local ply_manager = managers.player - if ply_manager then - - local carry_data = ply_manager:get_my_carry_data() - local carrying = carry_data and self:IsDrugsBag(carry_data) - self:ApplyAddictionDamage( ply_manager:player_unit(), carrying, dt ) - - end - - - end) - - Hooks:Add("PlayerDamageOnPostInit", self._playerDamageOnPostInitHook, function(ply, unit) - Queue:Add("AddictsSpawnFreeDrugs", function() - local loot = self._drug_spawns[math.random(1, #self._drug_spawns)] - managers.player:force_verify_carry() - self:SpawnMutatorLoot( loot ) - end, 1) - end) - -end - -function Mutator:OnDisabled() - Hooks:Remove( self._gameUpdateHook ) - Hooks:Remove( self._playerDamageOnPostInitHook ) -end - -function Mutator:SpawnMutatorLoot(loot_id, zipline_unit) - - local psuccess, perror = pcall(function() - - local carry_data = tweak_data.carry[loot_id] - if not carry_data then - return - end - - local player = managers.player:player_unit() - if player then - player:sound():play("Play_bag_generic_throw", nil, false) - else - return - end - - local camera_ext = player:camera() - local dye_initiated = carry_data.dye_initiated - local has_dye_pack = carry_data.has_dye_pack - local dye_value_multiplier = carry_data.dye_value_multiplier - local throw_distance_multiplier_upgrade_level = managers.player:upgrade_level("carry", "throw_distance_multiplier", 0) - - local pos = camera_ext:position() - local rot = camera_ext:rotation() - local peer_id = managers.network:session():local_peer() or 0 - - if not Network:is_client() then - managers.player:server_drop_carry(loot_id, carry_data.multiplier, dye_initiated, has_dye_pack, dye_value_multiplier, pos, rot, player:camera():forward(), throw_distance_multiplier_upgrade_level, zipline_unit, managers.network:session():local_peer():id()) - end - - end) - if not psuccess then - Print("[Error] " .. perror) - end - -end - --- END OF FILE diff --git a/GoonBase/mutators/mutator_all_bulldozers.lua b/GoonBase/mutators/mutator_all_bulldozers.lua deleted file mode 100644 index d87e67e..0000000 --- a/GoonBase/mutators/mutator_all_bulldozers.lua +++ /dev/null @@ -1,162 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -local Mutator = class(BaseMutator) -Mutator.Id = "AllBulldozerSpawns" -Mutator.OptionsName = "Bomb Squad" -Mutator.OptionsDesc = "Replace all spawning units with various Bulldozers and backup" -Mutator.Incompatibilities = { "AllCloakerSpawns", "AllTaserSpawns", "AllShieldSpawns" } - -Mutator.HookTaskData = "GroupAITweakDataPostInitTaskData_AllBulldozerMutator" -Mutator.HookUnitCategories = "GroupAITweakDataPostInitUnitCategories_AllBulldozerMutator" - -Hooks:Add("GoonBaseRegisterMutators", "GoonBaseRegisterMutators_AllBulldozerSpawns", function() - GoonBase.Mutators:RegisterMutator( Mutator ) -end) - -function Mutator:OnEnabled() - - Hooks:Add("GroupAITweakDataPostInitTaskData", self.HookTaskData, function(data, difficulty_index, difficulty) - self:ModifyTaskData(data, difficulty_index, difficulty) - end) - Hooks:Add("GroupAITweakDataPostInitUnitCategories", self.HookUnitCategories, function(data, difficulty_index) - self:ModifyUnitCategories(data, difficulty_index) - end) - -end - -function Mutator:OnDisabled() - Hooks:Remove(self.HookTaskData) - Hooks:Remove(self.HookUnitCategories) -end - -function Mutator:ModifyTaskData(data, difficulty_index, difficulty) - - data.besiege.recurring_group_SO = { - recurring_cloaker_spawn = { - interval = {15, 20}, - retire_delay = 3000 - }, - recurring_spawn_1 = { - interval = {3, 6} - } - } - - data.besiege.assault.groups = { - FBI_swats = { - 0.7, - 0.2, - 0.05 - }, - FBI_heavys = { - 0.7, - 0.2, - 0.05 - }, - FBI_shields = { - 0.7, - 0.2, - 0.05 - }, - FBI_tanks = { - 1, - 0.4, - 0.15 - }, - CS_tazers = { - 0.7, - 0.2, - 0.05 - }, - FBI_spoocs = { - 0.7, - 0.2, - 0.05 - }, - single_spooc = { - 0.7, - 0.2, - 0.05 - } - } - - data.besiege.reenforce.groups = { - CS_defend_a = { - 0.7, - 0.2, - 0.05 - }, - FBI_defend_b = { - 0.7, - 0.2, - 0.05 - }, - FBI_defend_c = { - 0.7, - 0.2, - 0.05 - }, - FBI_defend_d = { - 0.7, - 0.2, - 0.05 - } - } - - data.besiege.recon.groups = { - FBI_stealth_a = { - 0.7, - 0.2, - 0.05 - }, - FBI_stealth_b = { - 0.7, - 0.2, - 0.05 - }, - single_spooc = { - 0.7, - 0.2, - 0.05 - } - } - -end - -function Mutator:ModifyUnitCategories(data, difficulty_index) - - local access_type_walk_only = { walk = true } - - data.special_unit_spawn_limits = { - tank = 1000000, - taser = 0, - spooc = 0, - shield = 0, - } - - data.unit_categories.FBI_tank.units = { - Idstring("units/payday2/characters/ene_bulldozer_1/ene_bulldozer_1"), - Idstring("units/payday2/characters/ene_bulldozer_2/ene_bulldozer_2"), - Idstring("units/payday2/characters/ene_bulldozer_3/ene_bulldozer_3") - } - data.unit_categories.CS_cop_C45_R870 = data.unit_categories.FBI_tank - data.unit_categories.CS_cop_stealth_MP5 = data.unit_categories.FBI_tank - data.unit_categories.CS_swat_MP5 = data.unit_categories.FBI_tank - data.unit_categories.CS_swat_R870 = data.unit_categories.FBI_tank - data.unit_categories.CS_heavy_M4 = data.unit_categories.FBI_tank - data.unit_categories.CS_heavy_M4_w = data.unit_categories.FBI_tank - data.unit_categories.FBI_suit_C45_M4 = data.unit_categories.FBI_tank - data.unit_categories.FBI_suit_M4_MP5 = data.unit_categories.FBI_tank - data.unit_categories.FBI_suit_stealth_MP5 = data.unit_categories.FBI_tank - data.unit_categories.FBI_swat_M4 = data.unit_categories.FBI_tank - data.unit_categories.FBI_swat_R870 = data.unit_categories.FBI_tank - data.unit_categories.FBI_heavy_G36 = data.unit_categories.FBI_tank - data.unit_categories.FBI_heavy_G36_w = data.unit_categories.FBI_tank - data.unit_categories.CS_tazer = data.unit_categories.spooc - data.unit_categories.CS_shield = data.unit_categories.spooc - data.unit_categories.FBI_shield = data.unit_categories.spooc - -end --- END OF FILE diff --git a/GoonBase/mutators/mutator_all_cloakers.lua b/GoonBase/mutators/mutator_all_cloakers.lua deleted file mode 100644 index d416af1..0000000 --- a/GoonBase/mutators/mutator_all_cloakers.lua +++ /dev/null @@ -1,163 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -local Mutator = class(BaseMutator) -Mutator.Id = "AllCloakerSpawns" -Mutator.OptionsName = "Night of a Million Cloakers" -Mutator.OptionsDesc = "Replace all spawning units with Cloakers" -Mutator.Incompatibilities = { "AllTaserSpawns", "AllBulldozerSpawns", "AllShieldSpawns" } - -Mutator.HookTaskData = "GroupAITweakDataPostInitTaskData_AllCloakersMutator" -Mutator.HookUnitCategories = "GroupAITweakDataPostInitUnitCategories_AllCloakersMutator" - -Hooks:Add("GoonBaseRegisterMutators", "GoonBaseRegisterMutators_AllCloakerSpawns", function() - GoonBase.Mutators:RegisterMutator( Mutator ) -end) - -function Mutator:OnEnabled() - - Hooks:Add("GroupAITweakDataPostInitTaskData", self.HookTaskData, function(data, difficulty_index, difficulty) - self:ModifyTaskData(data, difficulty_index, difficulty) - end) - Hooks:Add("GroupAITweakDataPostInitUnitCategories", self.HookUnitCategories, function(data, difficulty_index) - self:ModifyUnitCategories(data, difficulty_index) - end) - -end - -function Mutator:OnDisabled() - Hooks:Remove(self.HookTaskData) - Hooks:Remove(self.HookUnitCategories) -end - -function Mutator:ModifyTaskData(data, difficulty_index, difficulty) - - data.besiege.recurring_group_SO = { - recurring_cloaker_spawn = { - interval = {0, 1}, - retire_delay = 30 - }, - recurring_spawn_1 = { - interval = {0, 1} - } - } - - data.besiege.assault.groups = { - FBI_swats = { - 1, - 1, - 1 - }, - FBI_heavys = { - 1, - 1, - 1 - }, - FBI_shields = { - 1, - 1, - 1 - }, - FBI_tanks = { - 1, - 1, - 1 - }, - CS_tazers = { - 1, - 1, - 1 - }, - FBI_spoocs = { - 1, - 1, - 1 - }, - single_spooc = { - 1, - 1, - 1 - } - } - - data.besiege.reenforce.groups = { - CS_defend_a = { - 1, - 1, - 1 - }, - FBI_defend_b = { - 1, - 1, - 1 - }, - FBI_defend_c = { - 1, - 1, - 1 - }, - FBI_defend_d = { - 1, - 1, - 1 - } - } - - data.besiege.recon.groups = { - FBI_stealth_a = { - 1, - 1, - 1 - }, - FBI_stealth_b = { - 1, - 1, - 1 - }, - single_spooc = { - 1, - 1, - 1 - } - } - -end - -function Mutator:ModifyUnitCategories(data, difficulty_index) - - local access_type_all = { walk = true, acrobatic = true } - - data.special_unit_spawn_limits = { - tank = 0, - taser = 0, - spooc = 1000000, - shield = 0 - } - - data.unit_categories.spooc.units = { - Idstring("units/payday2/characters/ene_spook_1/ene_spook_1"), - Idstring("units/payday2/characters/ene_spook_1/ene_spook_1"), - Idstring("units/payday2/characters/ene_spook_1/ene_spook_1") - } - data.unit_categories.CS_cop_C45_R870 = data.unit_categories.spooc - data.unit_categories.CS_cop_stealth_MP5 = data.unit_categories.spooc - data.unit_categories.CS_swat_MP5 = data.unit_categories.spooc - data.unit_categories.CS_swat_R870 = data.unit_categories.spooc - data.unit_categories.CS_heavy_M4 = data.unit_categories.spooc - data.unit_categories.CS_heavy_M4_w = data.unit_categories.spooc - data.unit_categories.CS_tazer = data.unit_categories.spooc - data.unit_categories.CS_shield = data.unit_categories.spooc - data.unit_categories.FBI_suit_C45_M4 = data.unit_categories.spooc - data.unit_categories.FBI_suit_M4_MP5 = data.unit_categories.spooc - data.unit_categories.FBI_suit_stealth_MP5 = data.unit_categories.spooc - data.unit_categories.FBI_swat_M4 = data.unit_categories.spooc - data.unit_categories.FBI_swat_R870 = data.unit_categories.spooc - data.unit_categories.FBI_heavy_G36 = data.unit_categories.spooc - data.unit_categories.FBI_heavy_G36_w = data.unit_categories.spooc - data.unit_categories.FBI_shield = data.unit_categories.spooc - data.unit_categories.FBI_tank = data.unit_categories.spooc - -end --- END OF FILE diff --git a/GoonBase/mutators/mutator_all_shields.lua b/GoonBase/mutators/mutator_all_shields.lua deleted file mode 100644 index b1e56d3..0000000 --- a/GoonBase/mutators/mutator_all_shields.lua +++ /dev/null @@ -1,162 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -local Mutator = class(BaseMutator) -Mutator.Id = "AllShieldSpawns" -Mutator.OptionsName = "Spartan Army" -Mutator.OptionsDesc = "Replace all spawning units with Shields" -Mutator.Incompatibilities = { "AllCloakerSpawns", "AllTaserSpawns", "AllBulldozerSpawns" } - -Mutator.HookTaskData = "GroupAITweakDataPostInitTaskData_AllShieldsMutator" -Mutator.HookUnitCategories = "GroupAITweakDataPostInitUnitCategories_AllShieldsMutator" - -Hooks:Add("GoonBaseRegisterMutators", "GoonBaseRegisterMutators_AllShieldSpawns", function() - GoonBase.Mutators:RegisterMutator( Mutator ) -end) - -function Mutator:OnEnabled() - - Hooks:Add("GroupAITweakDataPostInitTaskData", self.HookTaskData, function(data, difficulty_index, difficulty) - self:ModifyTaskData(data, difficulty_index, difficulty) - end) - Hooks:Add("GroupAITweakDataPostInitUnitCategories", self.HookUnitCategories, function(data, difficulty_index) - self:ModifyUnitCategories(data, difficulty_index) - end) - -end - -function Mutator:OnDisabled() - Hooks:Remove(self.HookTaskData) - Hooks:Remove(self.HookUnitCategories) -end - -function Mutator:ModifyTaskData(data, difficulty_index, difficulty) - - data.besiege.recurring_group_SO = { - recurring_cloaker_spawn = { - interval = {0, 1}, - retire_delay = 30 - }, - recurring_spawn_1 = { - interval = {0, 1} - } - } - - data.besiege.assault.groups = { - FBI_swats = { - 1, - 1, - 1 - }, - FBI_heavys = { - 1, - 1, - 1 - }, - FBI_shields = { - 1, - 1, - 1 - }, - FBI_tanks = { - 1, - 1, - 1 - }, - CS_tazers = { - 1, - 1, - 1 - }, - FBI_spoocs = { - 1, - 1, - 1 - }, - single_spooc = { - 1, - 1, - 1 - } - } - - data.besiege.reenforce.groups = { - CS_defend_a = { - 1, - 1, - 1 - }, - FBI_defend_b = { - 1, - 1, - 1 - }, - FBI_defend_c = { - 1, - 1, - 1 - }, - FBI_defend_d = { - 1, - 1, - 1 - } - } - - data.besiege.recon.groups = { - FBI_stealth_a = { - 1, - 1, - 1 - }, - FBI_stealth_b = { - 1, - 1, - 1 - }, - single_spooc = { - 1, - 1, - 1 - } - } - -end - -function Mutator:ModifyUnitCategories(data, difficulty_index) - - local access_type_all = { walk = true, acrobatic = true } - - data.special_unit_spawn_limits = { - tank = 0, - taser = 0, - spooc = 0, - shield = 1000000, - } - - data.unit_categories.FBI_shield.units = { - Idstring("units/payday2/characters/ene_shield_1/ene_shield_1"), - Idstring("units/payday2/characters/ene_shield_1/ene_shield_1"), - Idstring("units/payday2/characters/ene_shield_2/ene_shield_2") - } - data.unit_categories.CS_cop_C45_R870 = data.unit_categories.FBI_shield - data.unit_categories.CS_cop_stealth_MP5 = data.unit_categories.FBI_shield - data.unit_categories.CS_swat_MP5 = data.unit_categories.FBI_shield - data.unit_categories.CS_swat_R870 = data.unit_categories.FBI_shield - data.unit_categories.CS_heavy_M4 = data.unit_categories.FBI_shield - data.unit_categories.CS_heavy_M4_w = data.unit_categories.FBI_shield - data.unit_categories.CS_tazer = data.unit_categories.FBI_shield - data.unit_categories.CS_shield = data.unit_categories.FBI_shield - data.unit_categories.FBI_suit_C45_M4 = data.unit_categories.FBI_shield - data.unit_categories.FBI_suit_M4_MP5 = data.unit_categories.FBI_shield - data.unit_categories.FBI_suit_stealth_MP5 = data.unit_categories.FBI_shield - data.unit_categories.FBI_swat_M4 = data.unit_categories.FBI_shield - data.unit_categories.FBI_swat_R870 = data.unit_categories.FBI_shield - data.unit_categories.FBI_heavy_G36 = data.unit_categories.FBI_shield - data.unit_categories.FBI_heavy_G36_w = data.unit_categories.FBI_shield - data.unit_categories.FBI_tank = data.unit_categories.FBI_shield - -end --- END OF FILE diff --git a/GoonBase/mutators/mutator_all_tazers.lua b/GoonBase/mutators/mutator_all_tazers.lua deleted file mode 100644 index bebc40b..0000000 --- a/GoonBase/mutators/mutator_all_tazers.lua +++ /dev/null @@ -1,162 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -local Mutator = class(BaseMutator) -Mutator.Id = "AllTaserSpawns" -Mutator.OptionsName = "Danger, Danger! High Voltage!" -Mutator.OptionsDesc = "Replace all spawning units with Tasers" -Mutator.Incompatibilities = { "AllCloakerSpawns", "AllBulldozerSpawns", "AllShieldSpawns" } - -Mutator.HookTaskData = "GroupAITweakDataPostInitTaskData_AllTasersMutator" -Mutator.HookUnitCategories = "GroupAITweakDataPostInitUnitCategories_AllTasersMutator" - -Hooks:Add("GoonBaseRegisterMutators", "GoonBaseRegisterMutators_AllTaserSpawns", function() - GoonBase.Mutators:RegisterMutator( Mutator ) -end) - -function Mutator:OnEnabled() - - Hooks:Add("GroupAITweakDataPostInitTaskData", self.HookTaskData, function(data, difficulty_index, difficulty) - self:ModifyTaskData(data, difficulty_index, difficulty) - end) - Hooks:Add("GroupAITweakDataPostInitUnitCategories", self.HookUnitCategories, function(data, difficulty_index) - self:ModifyUnitCategories(data, difficulty_index) - end) - -end - -function Mutator:OnDisabled() - Hooks:Remove(self.HookTaskData) - Hooks:Remove(self.HookUnitCategories) -end - -function Mutator:ModifyTaskData(data, difficulty_index, difficulty) - - data.besiege.recurring_group_SO = { - recurring_cloaker_spawn = { - interval = {0, 1}, - retire_delay = 30 - }, - recurring_spawn_1 = { - interval = {0, 1} - } - } - - data.besiege.assault.groups = { - FBI_swats = { - 1, - 1, - 1 - }, - FBI_heavys = { - 1, - 1, - 1 - }, - FBI_shields = { - 1, - 1, - 1 - }, - FBI_tanks = { - 1, - 1, - 1 - }, - CS_tazers = { - 1, - 1, - 1 - }, - FBI_spoocs = { - 1, - 1, - 1 - }, - single_spooc = { - 1, - 1, - 1 - } - } - - data.besiege.reenforce.groups = { - CS_defend_a = { - 1, - 1, - 1 - }, - FBI_defend_b = { - 1, - 1, - 1 - }, - FBI_defend_c = { - 1, - 1, - 1 - }, - FBI_defend_d = { - 1, - 1, - 1 - } - } - - data.besiege.recon.groups = { - FBI_stealth_a = { - 1, - 1, - 1 - }, - FBI_stealth_b = { - 1, - 1, - 1 - }, - single_spooc = { - 1, - 1, - 1 - } - } - -end - -function Mutator:ModifyUnitCategories(data, difficulty_index) - - local access_type_all = { walk = true, acrobatic = true } - - data.special_unit_spawn_limits = { - tank = 0, - taser = 1000000, - spooc = 0, - shield = 0, - } - - data.unit_categories.CS_tazer.units = { - Idstring("units/payday2/characters/ene_tazer_1/ene_tazer_1"), - Idstring("units/payday2/characters/ene_tazer_1/ene_tazer_1"), - Idstring("units/payday2/characters/ene_tazer_1/ene_tazer_1") - } - data.unit_categories.CS_cop_C45_R870 = data.unit_categories.CS_tazer - data.unit_categories.CS_cop_stealth_MP5 = data.unit_categories.CS_tazer - data.unit_categories.CS_swat_MP5 = data.unit_categories.CS_tazer - data.unit_categories.CS_swat_R870 = data.unit_categories.CS_tazer - data.unit_categories.CS_heavy_M4 = data.unit_categories.CS_tazer - data.unit_categories.CS_heavy_M4_w = data.unit_categories.CS_tazer - data.unit_categories.CS_shield = data.unit_categories.CS_tazer - data.unit_categories.FBI_suit_C45_M4 = data.unit_categories.CS_tazer - data.unit_categories.FBI_suit_M4_MP5 = data.unit_categories.CS_tazer - data.unit_categories.FBI_suit_stealth_MP5 = data.unit_categories.CS_tazer - data.unit_categories.FBI_swat_M4 = data.unit_categories.CS_tazer - data.unit_categories.FBI_swat_R870 = data.unit_categories.CS_tazer - data.unit_categories.FBI_heavy_G36 = data.unit_categories.CS_tazer - data.unit_categories.FBI_heavy_G36_w = data.unit_categories.CS_tazer - data.unit_categories.FBI_shield = data.unit_categories.CS_tazer - data.unit_categories.FBI_tank = data.unit_categories.CS_tazer - -end --- END OF FILE diff --git a/GoonBase/mutators/mutator_exploding_bullets.lua b/GoonBase/mutators/mutator_exploding_bullets.lua deleted file mode 100644 index a07c627..0000000 --- a/GoonBase/mutators/mutator_exploding_bullets.lua +++ /dev/null @@ -1,65 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -local Mutator = class(BaseMutator) -Mutator.Id = "AllBulletsExplode" -Mutator.OptionsName = "4th of July" -Mutator.OptionsDesc = "Every bullet explodes and every person has them" -Mutator.AllPlayersRequireMod = true - -Mutator._WeaponBaseInit = "NewRaycastWeaponBaseInit_" .. Mutator:ID() -Mutator._CopDamage = "CopDamagePreDamageExplosion_" .. Mutator:ID() -Mutator._EnemyDamageMulitplier = 10 - -Hooks:Add("GoonBaseRegisterMutators", "GoonBaseRegisterMutators_" .. Mutator:ID(), function() - GoonBase.Mutators:RegisterMutator( Mutator ) -end) - -function Mutator:OnEnabled() - - Hooks:Add("NewRaycastWeaponBaseInit", self._WeaponBaseInit, function(weapon, unit) - - tweak_data.upgrades.explosive_bullet.curve_pow_orig = tweak_data.upgrades.explosive_bullet.curve_pow - tweak_data.upgrades.explosive_bullet.player_dmg_mul_orig = tweak_data.upgrades.explosive_bullet.player_dmg_mul - tweak_data.upgrades.explosive_bullet.range_orig = tweak_data.upgrades.explosive_bullet.range - - InstantExplosiveBulletBase.CURVE_POW = tweak_data.upgrades.explosive_bullet.curve_pow - InstantExplosiveBulletBase.PLAYER_DMG_MUL = tweak_data.upgrades.explosive_bullet.player_dmg_mul * 4 - InstantExplosiveBulletBase.RANGE = tweak_data.upgrades.explosive_bullet.range * 1.5 - - InstantBulletBase.CURVE_POW = tweak_data.upgrades.explosive_bullet.curve_pow_orig - InstantBulletBase.PLAYER_DMG_MUL = tweak_data.upgrades.explosive_bullet.player_dmg_mul_orig - InstantBulletBase.RANGE = tweak_data.upgrades.explosive_bullet.range_orig - InstantBulletBase.EFFECT_PARAMS = clone( InstantExplosiveBulletBase.EFFECT_PARAMS ) - - InstantBulletBase.on_collision = function(bullet, col_ray, weapon_unit, user_unit, damage, blank) - if damage then - damage = damage * Mutator._EnemyDamageMulitplier - end - InstantExplosiveBulletBase.on_collision(bullet, col_ray, weapon_unit, user_unit, damage, blank) - end - InstantBulletBase.on_collision_client = InstantExplosiveBulletBase.on_collision_client - - end) - - Hooks:Add("CopDamagePreDamageExplosion", self._CopDamage, function(cop, attack_data) - - if attack_data.attacker_unit and attack_data.attacker_unit:movement() then - local same_team = attack_data.attacker_unit:movement():team().id == "law1" - if same_team then - return true - end - end - attack_data.damage = attack_data.damage / Mutator._EnemyDamageMulitplier - - end) - -end - -function Mutator:OnDisabled() - Hooks:Remove(self._WeaponBaseInit) - Hooks:Remove(self._CopDamage) -end --- END OF FILE diff --git a/GoonBase/mutators/mutator_floating_bodies.lua b/GoonBase/mutators/mutator_floating_bodies.lua deleted file mode 100644 index 9745356..0000000 --- a/GoonBase/mutators/mutator_floating_bodies.lua +++ /dev/null @@ -1,81 +0,0 @@ ----------- --- Payday 2 GoonMod, Public Release Beta 2, built on 1/9/2015 9:30:33 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -local Mutator = class(BaseMutator) -Mutator.Id = "FloatingBodies" -Mutator.OptionsName = "All Cops go to Heaven" -Mutator.OptionsDesc = "Enemies will float upwards after death. Will not effect bodies with pagers." -Mutator.AllPlayersRequireMod = true - -Mutator._FloatTime = 60 -Mutator._ClientStealthFloatTime = 15 -Mutator._PostUpdateRagdolled = "CopActionHurtPostUpdateRagdolled_" .. Mutator:ID() - -Hooks:Add("GoonBaseRegisterMutators", "GoonBaseRegisterMutators_" .. Mutator:ID(), function() - GoonBase.Mutators:RegisterMutator( Mutator ) -end) - -function Mutator:OnEnabled() - - Hooks:Add("CopActionHurtPostUpdateRagdolled", self._PostUpdateRagdolled, function(cop, t) - - if not cop._death_time then - cop._death_time = t - end - local float_time = t - (cop._death_time or t) - local should_float = float_time < self._FloatTime - local float_timed_out = float_time > self._FloatTime - - if managers.groupai:state():whisper_mode() then - - if Network:is_server() then - if should_float and cop._unit:brain().is_pager_started then - should_float = not cop._unit:brain():is_pager_started() - end - else - should_float = float_time > self._ClientStealthFloatTime - end - - end - - if should_float then - - if not cop._death_twist then - cop._death_twist = math.random(2) == 1 and 1 or -1 - end - - local dt = TimerManager:game():delta_time() - local scale = 1.25 * dt - local unit = cop._unit - local height = 1 - local twist_dir = cop._death_twist - local rot_acc = (math.UP * (0.5 * twist_dir)) * -0.5 - local rot_time = 1 + math.rand(2) - local nr_u_bodies = unit:num_bodies() - local i_u_body = 0 - while nr_u_bodies > i_u_body do - local u_body = unit:body(i_u_body) - if u_body:enabled() and u_body:dynamic() then - local body_mass = u_body:mass() - World:play_physic_effect(Idstring("physic_effects/shotgun_hit"), u_body, math.UP * 600 * scale, 4 * body_mass / math.random(2), rot_acc, rot_time) - end - i_u_body = i_u_body + 1 - end - - end - - if float_timed_out then - cop:_freeze_ragdoll() - cop.update = nil - end - - end) - -end - -function Mutator:OnDisabled() - Hooks:Remove( self._PostUpdateRagdolled ) -end --- END OF FILE diff --git a/GoonBase/mutators/mutator_insane_spawnrate.lua b/GoonBase/mutators/mutator_insane_spawnrate.lua deleted file mode 100644 index c161b33..0000000 --- a/GoonBase/mutators/mutator_insane_spawnrate.lua +++ /dev/null @@ -1,455 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -local Mutator = class(BaseMutator) -Mutator.Id = "InsaneSpawnRate" -Mutator.OptionsName = "Excessive Force" -Mutator.OptionsDesc = "Increases the number of police that spawn" -Mutator.Incompatibilities = { "SuicidalSpawnRate", "SuicidalSpawnRateCops", "InsaneSpawnRateCops" } - -Mutator.HookSpawnGroups = "GroupAITweakDataPostInitEnemySpawnGroups_InsaneSpawnRate" -Mutator.HookSpawnCategories = "GroupAITweakDataPostInitUnitCategories_InsaneSpawnRate" - -Hooks:Add("GoonBaseRegisterMutators", "GoonBaseRegisterMutators_InsaneSpawnRate", function() - GoonBase.Mutators:RegisterMutator( Mutator ) -end) - -function Mutator:OnEnabled() - - Hooks:Add("GroupAITweakDataPostInitUnitCategories", self.HookSpawnCategories, function(data, difficulty_index) - self:ModifyUnitCategories(data, difficulty_index) - end) - Hooks:Add("GroupAITweakDataPostInitEnemySpawnGroups", self.HookSpawnGroups, function(data, difficulty_index) - self:ModifyTweakData(data, difficulty_index) - end) - -end - -function Mutator:OnDisabled() - Hooks:Remove(self.HookSpawnCategories) - Hooks:Remove(self.HookSpawnGroups) -end - -function Mutator:ModifyUnitCategories(data, difficulty_index) - - data.special_unit_spawn_limits = { - tank = 6, - taser = 18, - spooc = 12, - shield = 64, - } - -end - -function Mutator:ModifyTweakData(data, difficulty_index) - - local self = data - self.enemy_spawn_groups.CS_defend_a = { - amount = {9, 12}, - spawn = { - { - unit = "CS_cop_C45_R870", - freq = 3, - tactics = self._tactics.CS_cop, - rank = 1 - } - } - } - - self.enemy_spawn_groups.CS_defend_b = { - amount = {9, 12}, - spawn = { - { - unit = "CS_swat_MP5", - freq = 3, - amount_min = 3, - tactics = self._tactics.CS_cop, - rank = 1 - } - } - } - - self.enemy_spawn_groups.CS_defend_c = { - amount = {9, 12}, - spawn = { - { - unit = "CS_heavy_M4", - freq = 3, - amount_min = 3, - tactics = self._tactics.CS_cop, - rank = 1 - } - } - } - - self.enemy_spawn_groups.CS_cops = { - amount = {9, 12}, - spawn = { - { - unit = "CS_cop_C45_R870", - freq = 3, - amount_min = 3, - tactics = self._tactics.CS_cop, - rank = 1 - } - } - } - - self.enemy_spawn_groups.CS_stealth_a = { - amount = {6, 9}, - spawn = { - { - unit = "CS_cop_stealth_MP5", - freq = 3, - amount_min = 3, - tactics = self._tactics.CS_cop_stealth, - rank = 1 - } - } - } - - self.enemy_spawn_groups.CS_swats = { - amount = {9, 12}, - spawn = { - { - unit = "CS_swat_MP5", - freq = 3, - tactics = self._tactics.CS_swat_rifle, - rank = 2 - }, - { - unit = "CS_swat_R870", - freq = 1.5, - amount_max = 6, - tactics = self._tactics.CS_swat_shotgun, - rank = 1 - }, - { - unit = "CS_swat_MP5", - freq = 1, - tactics = self._tactics.CS_swat_rifle_flank, - rank = 3 - } - } - } - - self.enemy_spawn_groups.CS_heavys = { - amount = {9, 12}, - spawn = { - { - unit = "CS_heavy_M4", - freq = 3, - tactics = self._tactics.CS_swat_rifle, - rank = 2 - }, - { - unit = "CS_heavy_M4", - freq = 1.15, - tactics = self._tactics.CS_swat_rifle_flank, - rank = 3 - } - } - } - - self.enemy_spawn_groups.CS_shields = { - amount = {9, 12}, - spawn = { - { - unit = "CS_shield", - freq = 3, - amount_min = 3, - amount_max = 6, - tactics = self._tactics.CS_shield, - rank = 3 - }, - { - unit = "CS_cop_stealth_MP5", - freq = 1.5, - amount_max = 3, - tactics = self._tactics.CS_cop_stealth, - rank = 1 - }, - { - unit = "CS_heavy_M4_w", - freq = 2.5, - amount_max = 3, - tactics = self._tactics.CS_swat_heavy, - rank = 2 - } - } - } - - self.enemy_spawn_groups.CS_tazers = { - amount = {3, 9}, - spawn = { - { - unit = "CS_tazer", - freq = 3, - amount_min = 3, - amount_max = 3, - tactics = self._tactics.CS_tazer, - rank = 2 - }, - { - unit = "CS_swat_MP5", - freq = 3, - amount_max = 6, - tactics = self._tactics.CS_cop_stealth, - rank = 1 - } - } - } - - self.enemy_spawn_groups.CS_tanks = { - amount = {3, 6}, - spawn = { - { - unit = "FBI_tank", - freq = 3, - amount_min = 3, - tactics = self._tactics.FBI_tank, - rank = 2 - }, - { - unit = "CS_tazer", - freq = 1.5, - amount_max = 3, - tactics = self._tactics.CS_tazer, - rank = 1 - } - } - } - - self.enemy_spawn_groups.FBI_defend_a = { - amount = {9, 9}, - spawn = { - { - unit = "FBI_suit_C45_M4", - freq = 3, - amount_min = 3, - tactics = self._tactics.FBI_suit, - rank = 2 - }, - { - unit = "CS_cop_C45_R870", - freq = 3, - tactics = self._tactics.FBI_suit, - rank = 1 - } - } - } - - self.enemy_spawn_groups.FBI_defend_b = { - amount = {9, 9}, - spawn = { - { - unit = "FBI_suit_M4_MP5", - freq = 3, - amount_min = 3, - tactics = self._tactics.FBI_suit, - rank = 2 - }, - { - unit = "FBI_swat_M4", - freq = 3, - tactics = self._tactics.FBI_suit, - rank = 1 - } - } - } - - self.enemy_spawn_groups.FBI_defend_c = { - amount = {9, 9}, - spawn = { - { - unit = "FBI_swat_M4", - freq = 3, - tactics = self._tactics.FBI_suit, - rank = 1 - } - } - } - - self.enemy_spawn_groups.FBI_defend_d = { - amount = {6, 9}, - spawn = { - { - unit = "FBI_heavy_G36", - freq = 3, - tactics = self._tactics.FBI_suit, - rank = 1 - } - } - } - - self.enemy_spawn_groups.FBI_stealth_a = { - amount = {6, 9}, - spawn = { - { - unit = "FBI_suit_stealth_MP5", - freq = 3, - amount_min = 3, - tactics = self._tactics.FBI_suit_stealth, - rank = 1 - }, - { - unit = "CS_tazer", - freq = 3, - amount_max = 6, - tactics = self._tactics.CS_tazer, - rank = 2 - } - } - } - - self.enemy_spawn_groups.FBI_stealth_b = { - amount = {6, 9}, - spawn = { - { - unit = "FBI_suit_stealth_MP5", - freq = 3, - amount_min = 3, - tactics = self._tactics.FBI_suit_stealth, - rank = 1 - }, - { - unit = "FBI_suit_M4_MP5", - freq = 2.5, - tactics = self._tactics.FBI_suit, - rank = 2 - } - } - } - - self.enemy_spawn_groups.FBI_swats = { - amount = {9, 12}, - spawn = { - { - unit = "FBI_swat_M4", - freq = 3, - amount_min = 3, - tactics = self._tactics.FBI_swat_rifle, - rank = 2 - }, - { - unit = "FBI_swat_M4", - freq = 2.5, - tactics = self._tactics.FBI_swat_rifle_flank, - rank = 3 - }, - { - unit = "FBI_swat_R870", - freq = 1.5, - amount_max = 6, - tactics = self._tactics.FBI_swat_shotgun, - rank = 1 - }, - { - unit = "spooc", - freq = 0.15, - amount_max = 6, - tactics = self._tactics.spooc, - rank = 1 - } - } - } - - self.enemy_spawn_groups.FBI_heavys = { - amount = {3, 6}, - spawn = { - { - unit = "FBI_heavy_G36", - freq = 3, - tactics = self._tactics.FBI_swat_rifle, - rank = 1 - }, - { - unit = "FBI_heavy_G36", - freq = 2.5, - tactics = self._tactics.FBI_swat_rifle_flank, - rank = 2 - }, - { - unit = "CS_tazer", - freq = 0.75, - amount_max = 3, - tactics = self._tactics.CS_tazer, - rank = 3 - } - } - } - - self.enemy_spawn_groups.FBI_shields = { - amount = {9, 12}, - spawn = { - { - unit = "FBI_shield", - freq = 3, - amount_min = 3, - amount_max = 6, - tactics = self._tactics.FBI_shield_flank, - rank = 3 - }, - { - unit = "CS_tazer", - freq = 2.5, - amount_max = 3, - tactics = self._tactics.CS_tazer, - rank = 2 - }, - { - unit = "FBI_heavy_G36", - freq = 1.5, - amount_max = 3, - tactics = self._tactics.FBI_swat_rifle_flank, - rank = 1 - } - } - } - - self.enemy_spawn_groups.FBI_tanks = { - amount = {6, 8}, - spawn = { - { - unit = "FBI_tank", - freq = 2, - amount_max = 3, - tactics = self._tactics.FBI_tank, - rank = 1 - }, - { - unit = "FBI_shield", - freq = 2, - amount_min = 3, - amount_max = 6, - tactics = self._tactics.FBI_shield_flank, - rank = 3 - }, - { - unit = "FBI_heavy_G36_w", - freq = 2.5, - amount_min = 3, - tactics = self._tactics.FBI_heavy_flank, - rank = 1 - } - } - } - - self.enemy_spawn_groups.single_spooc = { - amount = {2, 2}, - spawn = { - { - unit = "spooc", - freq = 2, - amount_min = 3, - tactics = self._tactics.spooc, - rank = 1 - } - } - } - self.enemy_spawn_groups.FBI_spoocs = self.enemy_spawn_groups.single_spooc - -end --- END OF FILE diff --git a/GoonBase/mutators/mutator_insane_spawnrate_cops.lua b/GoonBase/mutators/mutator_insane_spawnrate_cops.lua deleted file mode 100644 index 3df7e3b..0000000 --- a/GoonBase/mutators/mutator_insane_spawnrate_cops.lua +++ /dev/null @@ -1,298 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -local Mutator = class(BaseMutator) -Mutator.Id = "InsaneSpawnRateCops" -Mutator.OptionsName = "Excessive Force - Cops Only" -Mutator.OptionsDesc = "Increases the number of police that spawn" -Mutator.Incompatibilities = { "SuicidalSpawnRate", "SuicidalSpawnRateCops", "InsaneSpawnRate" } - -Mutator.HookSpawnGroups = "GroupAITweakDataPostInitEnemySpawnGroups_InsaneSpawnRateCops" - -Hooks:Add("GoonBaseRegisterMutators", "GoonBaseRegisterMutators_InsaneSpawnRateCops", function() - GoonBase.Mutators:RegisterMutator( Mutator ) -end) - -function Mutator:OnEnabled() - Hooks:Add("GroupAITweakDataPostInitEnemySpawnGroups", self.HookSpawnGroups, function(data, difficulty_index) - self:ModifyTweakData(data, difficulty_index) - end) -end - -function Mutator:OnDisabled() - Hooks:Remove(self.HookSpawnGroups) -end - -function Mutator:ModifyTweakData(data, difficulty_index) - - local self = data - self.enemy_spawn_groups.CS_defend_a = { - amount = {9, 12}, - spawn = { - { - unit = "CS_cop_C45_R870", - freq = 3, - tactics = self._tactics.CS_cop, - rank = 1 - } - } - } - - self.enemy_spawn_groups.CS_defend_b = { - amount = {9, 12}, - spawn = { - { - unit = "CS_swat_MP5", - freq = 3, - amount_min = 3, - tactics = self._tactics.CS_cop, - rank = 1 - } - } - } - - self.enemy_spawn_groups.CS_defend_c = { - amount = {9, 12}, - spawn = { - { - unit = "CS_heavy_M4", - freq = 3, - amount_min = 3, - tactics = self._tactics.CS_cop, - rank = 1 - } - } - } - - self.enemy_spawn_groups.CS_cops = { - amount = {9, 12}, - spawn = { - { - unit = "CS_cop_C45_R870", - freq = 3, - amount_min = 3, - tactics = self._tactics.CS_cop, - rank = 1 - } - } - } - - self.enemy_spawn_groups.CS_stealth_a = { - amount = {6, 9}, - spawn = { - { - unit = "CS_cop_stealth_MP5", - freq = 3, - amount_min = 3, - tactics = self._tactics.CS_cop_stealth, - rank = 1 - } - } - } - - self.enemy_spawn_groups.CS_swats = { - amount = {9, 12}, - spawn = { - { - unit = "CS_swat_MP5", - freq = 3, - tactics = self._tactics.CS_swat_rifle, - rank = 2 - }, - { - unit = "CS_swat_R870", - freq = 1.5, - amount_max = 6, - tactics = self._tactics.CS_swat_shotgun, - rank = 1 - }, - { - unit = "CS_swat_MP5", - freq = 1, - tactics = self._tactics.CS_swat_rifle_flank, - rank = 3 - } - } - } - - self.enemy_spawn_groups.CS_heavys = { - amount = {9, 12}, - spawn = { - { - unit = "CS_heavy_M4", - freq = 3, - tactics = self._tactics.CS_swat_rifle, - rank = 2 - }, - { - unit = "CS_heavy_M4", - freq = 1.15, - tactics = self._tactics.CS_swat_rifle_flank, - rank = 3 - } - } - } - - self.enemy_spawn_groups.FBI_defend_a = { - amount = {9, 9}, - spawn = { - { - unit = "FBI_suit_C45_M4", - freq = 3, - amount_min = 3, - tactics = self._tactics.FBI_suit, - rank = 2 - }, - { - unit = "CS_cop_C45_R870", - freq = 3, - tactics = self._tactics.FBI_suit, - rank = 1 - } - } - } - - self.enemy_spawn_groups.FBI_defend_b = { - amount = {9, 9}, - spawn = { - { - unit = "FBI_suit_M4_MP5", - freq = 3, - amount_min = 3, - tactics = self._tactics.FBI_suit, - rank = 2 - }, - { - unit = "FBI_swat_M4", - freq = 3, - tactics = self._tactics.FBI_suit, - rank = 1 - } - } - } - - self.enemy_spawn_groups.FBI_defend_c = { - amount = {9, 9}, - spawn = { - { - unit = "FBI_swat_M4", - freq = 3, - tactics = self._tactics.FBI_suit, - rank = 1 - } - } - } - - self.enemy_spawn_groups.FBI_defend_d = { - amount = {6, 9}, - spawn = { - { - unit = "FBI_heavy_G36", - freq = 3, - tactics = self._tactics.FBI_suit, - rank = 1 - } - } - } - - self.enemy_spawn_groups.FBI_stealth_a = { - amount = {6, 9}, - spawn = { - { - unit = "FBI_suit_stealth_MP5", - freq = 3, - amount_min = 3, - tactics = self._tactics.FBI_suit_stealth, - rank = 1 - }, - { - unit = "CS_tazer", - freq = 1, - amount_max = 1, - tactics = self._tactics.CS_tazer, - rank = 2 - } - } - } - - self.enemy_spawn_groups.FBI_stealth_b = { - amount = {6, 9}, - spawn = { - { - unit = "FBI_suit_stealth_MP5", - freq = 3, - amount_min = 3, - tactics = self._tactics.FBI_suit_stealth, - rank = 1 - }, - { - unit = "FBI_suit_M4_MP5", - freq = 2.5, - tactics = self._tactics.FBI_suit, - rank = 2 - } - } - } - - self.enemy_spawn_groups.FBI_swats = { - amount = {9, 12}, - spawn = { - { - unit = "FBI_swat_M4", - freq = 3, - amount_min = 3, - tactics = self._tactics.FBI_swat_rifle, - rank = 2 - }, - { - unit = "FBI_swat_M4", - freq = 2.5, - tactics = self._tactics.FBI_swat_rifle_flank, - rank = 3 - }, - { - unit = "FBI_swat_R870", - freq = 1.5, - amount_max = 6, - tactics = self._tactics.FBI_swat_shotgun, - rank = 1 - }, - { - unit = "spooc", - freq = 0.15, - amount_max = 1, - tactics = self._tactics.spooc, - rank = 1 - } - } - } - - self.enemy_spawn_groups.FBI_heavys = { - amount = {3, 6}, - spawn = { - { - unit = "FBI_heavy_G36", - freq = 3, - tactics = self._tactics.FBI_swat_rifle, - rank = 1 - }, - { - unit = "FBI_heavy_G36", - freq = 2.5, - tactics = self._tactics.FBI_swat_rifle_flank, - rank = 2 - }, - { - unit = "CS_tazer", - freq = 0.75, - amount_max = 1, - tactics = self._tactics.CS_tazer, - rank = 3 - } - } - } - -end --- END OF FILE diff --git a/GoonBase/mutators/mutator_instagib.lua b/GoonBase/mutators/mutator_instagib.lua deleted file mode 100644 index 97844a9..0000000 --- a/GoonBase/mutators/mutator_instagib.lua +++ /dev/null @@ -1,43 +0,0 @@ ----------- --- Payday 2 GoonMod, Public Release Beta 2, built on 1/9/2015 9:30:33 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -local Mutator = class(BaseMutator) -Mutator.Id = "Instagib" -Mutator.OptionsName = "Instagib" -Mutator.OptionsDesc = "All weapon damage is amplified to lethal levels" -Mutator.AllPlayersRequireMod = true - -Mutator._RWChkID = "NewRaycastWeaponBase_" .. Mutator.Id -Mutator._RWChkIDPost = "NewRaycastWeaponBase_PostHook_" .. Mutator.Id -Mutator._NPChkID = "NPCRaycastWeaponBase_" .. Mutator.Id - -Hooks:Add("GoonBaseRegisterMutators", "GoonBaseRegisterMutators_" .. Mutator.Id, function() - GoonBase.Mutators:RegisterMutator( Mutator ) -end) - -function Mutator:OnEnabled() - - Hooks:Add("NewRaycastWeaponBaseInit", self._RWChkID, function(weapon, unit) - - Hooks:PostHook(NewRaycastWeaponBase, "_get_current_damage", self._RWChkIDPost, function(self, dmg_mul) - return math.huge - end) - - end) - - Hooks:Add("NPCRaycastWeaponBaseInit", self._NPChkID, function(weapon, unit) - weapon._damage = math.huge - end) - -end - -function Mutator:OnDisabled() - - Hooks:Remove(self._RWChkID) - Hooks:RemovePostHook(self._RWChkIDPost) - Hooks:Remove(self._NPChkID) - -end --- END OF FILE diff --git a/GoonBase/mutators/mutator_jamming_weapons.lua b/GoonBase/mutators/mutator_jamming_weapons.lua deleted file mode 100644 index a52e73e..0000000 --- a/GoonBase/mutators/mutator_jamming_weapons.lua +++ /dev/null @@ -1,42 +0,0 @@ ----------- --- Payday 2 GoonMod, Public Release Beta 2, built on 1/9/2015 9:30:33 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -local Mutator = class(BaseMutator) -Mutator.Id = "WeaponsJam" -Mutator.OptionsName = "Crappy Weapons" -Mutator.OptionsDesc = "You thought the drills were bad? Weapons randomly jam when firing!" -Mutator.AllPlayersRequireMod = true - -Mutator._WeaponFirehkID = "NewRaycastWeaponBase_" .. Mutator.Id -Mutator._WeaponFirehkIDPost = "NewRaycastWeaponBase_Post_" .. Mutator.Id - -Hooks:Add("GoonBaseRegisterMutators", "GoonBaseRegisterMutators_" .. Mutator.Id, function() - GoonBase.Mutators:RegisterMutator( Mutator ) -end) - -function Mutator:OnEnabled() - - Hooks:Add("NewRaycastWeaponBaseInit", self._WeaponFirehkID, function(weapon, unit) - - Hooks:PostHook(NewRaycastWeaponBase, "fire", self._WeaponFirehkIDPost, function(weapon, from_pos, direction, dmg_mul, shoot_player, spread_mul, autohit_mul, suppr_mul, target_unit) - local roll = math.rand(1) - local chance = 0.12 - local gun = weapon.parent_weapon and weapon.parent_weapon:base() or weapon - if roll < chance then - gun:set_ammo_remaining_in_clip(0) - --one day we'll mosin ping sound but until then, EXPLOSIONSSSSSSSSSS - managers.explosion:play_sound_and_effects(weapon._unit:get_object(Idstring("a_shell")):position(), Vector3(), 0) - end - end) - - end) - -end - -function Mutator:OnDisabled() - Hooks:Remove(self._WeaponFirehkID) - Hooks:RemovePostHook(self._WeaponFirehkIDPost) -end --- END OF FILE diff --git a/GoonBase/mutators/mutator_lightning_speed.lua b/GoonBase/mutators/mutator_lightning_speed.lua deleted file mode 100644 index 0fc97dc..0000000 --- a/GoonBase/mutators/mutator_lightning_speed.lua +++ /dev/null @@ -1,135 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -local Mutator = class(BaseMutator) -Mutator.Id = "AllLightningSpeed" -Mutator.OptionsName = "Blitzkrieg" -Mutator.OptionsDesc = "All police units move lightning fast" - -Hooks:Add("GoonBaseRegisterMutators", "GoonBaseRegisterMutators_AllLightningSpeed", function() - GoonBase.Mutators:RegisterMutator( Mutator ) -end) - -Mutator.Units = {} -Mutator.Units.Cop = "CharacterTweakDataPostInitCop_" .. Mutator:ID() -Mutator.Units.FBI = "CharacterTweakDataPostInitFBI_" .. Mutator:ID() -Mutator.Units.SWAT = "CharacterTweakDataPostInitSWAT_" .. Mutator:ID() -Mutator.Units.HeavySWAT = "CharacterTweakDataPostInitHeavySWAT_" .. Mutator:ID() -Mutator.Units.FBISWAT = "CharacterTweakDataPostInitFBISWAT_" .. Mutator:ID() -Mutator.Units.FBIHeavySWAT = "CharacterTweakDataPostInitFBIHeavySWAT_" .. Mutator:ID() -Mutator.Units.CitySWAT = "CharacterTweakDataPostInitCitySWAT_" .. Mutator:ID() -Mutator.Units.Shield = "CharacterTweakDataPostInitShield_" .. Mutator:ID() -Mutator.Units.Taser = "CharacterTweakDataPostInitTaser_" .. Mutator:ID() -Mutator.Units.Tank = "CharacterTweakDataPostInitTank_" .. Mutator:ID() -Mutator.Units.GeneralSpeed = "CharacterTweakDataPostMultiplyAllSpeeds_" .. Mutator:ID() -Mutator.Units.SpawnCategories = "GroupAITweakDataPostInitUnitCategories_" .. Mutator:ID() - -function Mutator:OnEnabled() - - Hooks:Add("CharacterTweakDataPostInitCop", self.Units.Cop, function(data, presets) - data.cop.move_speed = presets.move_speed.lightning - end) - - Hooks:Add("CharacterTweakDataPostInitFBI", self.Units.FBI, function(data, presets) - data.fbi.move_speed = presets.move_speed.lightning - end) - - Hooks:Add("CharacterTweakDataPostInitSWAT", self.Units.SWAT, function(data, presets) - data.swat.move_speed = presets.move_speed.lightning - end) - - Hooks:Add("CharacterTweakDataPostInitHeavySWAT", self.Units.HeavySWAT, function(data, presets) - data.heavy_swat.move_speed = presets.move_speed.lightning - end) - - Hooks:Add("CharacterTweakDataPostInitFBISWAT", self.Units.FBISWAT, function(data, presets) - data.fbi_swat.move_speed = presets.move_speed.lightning - end) - - Hooks:Add("CharacterTweakDataPostInitFBIHeavySWAT", self.Units.FBIHeavySWAT, function(data, presets) - data.fbi_heavy_swat.move_speed = presets.move_speed.lightning - end) - - Hooks:Add("CharacterTweakDataPostInitCitySWAT", self.Units.CitySWAT, function(data, presets) - data.city_swat.move_speed = presets.move_speed.lightning - end) - - Hooks:Add("CharacterTweakDataPostInitShield", self.Units.Shield, function(data, presets) - data.shield.move_speed = presets.move_speed.lightning - end) - - Hooks:Add("CharacterTweakDataPostInitTaser", self.Units.Taser, function(data, presets) - data.taser.move_speed = presets.move_speed.lightning - end) - - Hooks:Add("CharacterTweakDataPostInitTank", self.Units.Tank, function(data, presets) - data.tank.move_speed = presets.move_speed.lightning - end) - - Hooks:Add("CharacterTweakDataPostMultiplyAllSpeeds", self.Units.GeneralSpeed, function(data, walk_mul, run_mul) - - walk_mul = 3 - run_mul = 5 - - local self = data - local all_units = { - "security", - "cop", - "fbi", - "swat", - "heavy_swat", - "sniper", - "gangster", - "tank", - "spooc", - "shield", - "taser" - } - for _, name in ipairs(all_units) do - local speed_table = self[name].SPEED_WALK - speed_table.hos = speed_table.hos * walk_mul - speed_table.cbt = speed_table.cbt * walk_mul - end - self.security.SPEED_RUN = self.security.SPEED_RUN * run_mul - self.cop.SPEED_RUN = self.cop.SPEED_RUN * run_mul - self.fbi.SPEED_RUN = self.fbi.SPEED_RUN * run_mul - self.swat.SPEED_RUN = self.swat.SPEED_RUN * run_mul - self.heavy_swat.SPEED_RUN = self.heavy_swat.SPEED_RUN * run_mul - self.fbi_heavy_swat.SPEED_RUN = self.fbi_heavy_swat.SPEED_RUN * run_mul - self.sniper.SPEED_RUN = self.sniper.SPEED_RUN * run_mul - self.gangster.SPEED_RUN = self.gangster.SPEED_RUN * run_mul - self.tank.SPEED_RUN = self.tank.SPEED_RUN * run_mul - self.spooc.SPEED_RUN = self.spooc.SPEED_RUN * run_mul - self.shield.SPEED_RUN = self.shield.SPEED_RUN * run_mul - self.taser.SPEED_RUN = self.taser.SPEED_RUN * run_mul - self.biker_escape.SPEED_RUN = self.biker_escape.SPEED_RUN * run_mul - - end) - - Hooks:Add("GroupAITweakDataPostInitUnitCategories", self.Units.SpawnCategories, function(data, difficulty_index) - local access_type_all = {walk = true, acrobatic = true} - data.unit_categories.FBI_shield.access = access_type_all - data.unit_categories.FBI_tank.access = access_type_all - data.unit_categories.CS_tazer.access = access_type_all - data.unit_categories.CS_shield.access = access_type_all - end) - -end - -function Mutator:OnDisabled() - Hooks:Remove(self.Units.Cop) - Hooks:Remove(self.Units.FBI) - Hooks:Remove(self.Units.SWAT) - Hooks:Remove(self.Units.HeavySWAT) - Hooks:Remove(self.Units.FBISWAT) - Hooks:Remove(self.Units.FBIHeavySWAT) - Hooks:Remove(self.Units.CitySWAT) - Hooks:Remove(self.Units.Shield) - Hooks:Remove(self.Units.Taser) - Hooks:Remove(self.Units.Tank) - Hooks:Remove(self.Units.GeneralSpeed) - Hooks:Remove(self.Units.SpawnCategories) -end --- END OF FILE diff --git a/GoonBase/mutators/mutator_no_ammo_pickups.lua b/GoonBase/mutators/mutator_no_ammo_pickups.lua deleted file mode 100644 index 27d9219..0000000 --- a/GoonBase/mutators/mutator_no_ammo_pickups.lua +++ /dev/null @@ -1,74 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 1/3/2015 12:28:05 AM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -local Mutator = class(BaseMutator) -Mutator.Id = "NoAmmoPickups" -Mutator.OptionsName = "Rationing" -Mutator.OptionsDesc = "Enemies no longer drop ammo. Bulldozers have a chance to drop an ammo bag where they die." -Mutator.AllPlayersRequireMod = true - -Mutator.CopDamageInit = "CopDamagePostInitialize_" .. Mutator:ID() -Mutator.CopPostDeath = "CopDamagePostDeath_" .. Mutator:ID() - -Mutator.DozersKilledSinceAmmo = 0 -Mutator.DropAmmoChance = { - [1] = 0.08, - [2] = 0.15, - [3] = 0.30, - [4] = 0.65, - [5] = 1.00, -} - -Hooks:Add("GoonBaseRegisterMutators", "GoonBaseRegisterMutators_" .. Mutator:ID(), function() - GoonBase.Mutators:RegisterMutator( Mutator ) -end) - -function Mutator:OnEnabled() - - Hooks:Add("CopDamagePostInitialize", self.CopDamageInit, function(cop, unit) - cop:set_pickup(nil) - end) - - Hooks:Add("CopDamagePostDeath", self.CopPostDeath, function(cop, variant) - local cop_type = cop._unit:base()._tweak_table - if cop_type == "tank" then - Mutator:CheckSpawnAmmoBag( cop._unit ) - end - end) - -end - -function Mutator:OnDisabled() - Hooks:Remove(self.CopDamageInit) - Hooks:Remove(self.CopPostDeath) -end - -function Mutator:CheckSpawnAmmoBag(unit) - - if GoonBase.Network:IsMultiplayer() and GoonBase.Network:IsHost() then - - self.DozersKilledSinceAmmo = self.DozersKilledSinceAmmo + 1 - local dozers = self.DozersKilledSinceAmmo - if dozers > #self.DropAmmoChance then - dozers = #self.DropAmmoChance - end - - if self.DropAmmoChance[ self.DozersKilledSinceAmmo ] > math.random() then - Mutator:SpawnAmmoBag(unit) - self.DozersKilledSinceAmmo = 0 - end - - end - -end - -function Mutator:SpawnAmmoBag(unit) - - local pos = unit:position() - local rot = Rotation(unit:movement():m_head_rot():yaw(), 0, 0) - AmmoBagBase.spawn(pos, rot, 0) - -end --- END OF FILE diff --git a/GoonBase/mutators/mutator_realism_mode.lua b/GoonBase/mutators/mutator_realism_mode.lua deleted file mode 100644 index 49fee68..0000000 --- a/GoonBase/mutators/mutator_realism_mode.lua +++ /dev/null @@ -1,56 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -local Mutator = class(BaseMutator) -Mutator.Id = "RealismMode" -Mutator.OptionsName = "Realism Mode" -Mutator.OptionsDesc = "No waypoints, no outlines, no names" -Mutator.AllPlayersRequireMod = true - -Hooks:Add("GoonBaseRegisterMutators", "GoonBaseRegisterMutators_" .. Mutator.Id, function() - GoonBase.Mutators:RegisterMutator( Mutator ) -end) - -Mutator._AddWaypointHook = "HUDManagerPreAddWaypoint_" .. Mutator.Id -Mutator._ContourInit = "ContourExtPreInitialize_" .. Mutator.Id -Mutator._ContourAdd = "ContourExtPreAdd_" .. Mutator.Id -Mutator._BaseInteractionSetContour = "BaseInteractionExtPreSetContour_" .. Mutator.Id -Mutator._AddNameLabel = "HUDManagerPreAddNameLabel_" .. Mutator.Id - -function Mutator:OnEnabled() - - Hooks:Add("HUDManagerPreAddWaypoint", self._AddWaypointHook, function(hud, id, data) - return true - end) - - Hooks:Add("ContourExtPreInitialize", self._ContourInit, function(contour, unit) - for k, v in pairs(ContourExt._types) do - if v.color ~= nil then - v.color = Color(0, 0, 0, 0) - end - end - end) - - Hooks:Add("ContourExtPreAdd", self._ContourAdd, function(contour, type, sync, multiplier) - contour._contour_list = contour._contour_list or {} - return true - end) - - Hooks:Add("BaseInteractionExtPreSetContour", self._BaseInteractionSetContour, function(int, color, opacity) - return { color = color, opacity = 0 } - end) - - Hooks:Add("HUDManagerPreAddNameLabel", self._AddNameLabel, function(hud, data) - data.name = "" - end) - -end - -function Mutator:OnDisabled() - Hooks:Remove(self._AddWaypointHook) - Hooks:Remove(self._ContourInit) - Hooks:Remove(self._BaseInteractionSetContour) -end --- END OF FILE diff --git a/GoonBase/mutators/mutator_shielddozers.lua b/GoonBase/mutators/mutator_shielddozers.lua deleted file mode 100644 index 4d1abd4..0000000 --- a/GoonBase/mutators/mutator_shielddozers.lua +++ /dev/null @@ -1,61 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 1/3/2015 12:28:05 AM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -local Mutator = class(BaseMutator) -Mutator.Id = "BulldozersWithShields" -Mutator.OptionsName = "Armour Plating" -Mutator.OptionsDesc = "Bulldozers spawn with shields" -Mutator.AllPlayersRequireMod = true - -Mutator.CheckShield = "CopInventoryCheckSpawnShield_" .. Mutator:ID() -Mutator.UpdateShield = "CopInventoryUpdate_" .. Mutator:ID() - -Mutator.ShieldUnits = { - [1] = "units/payday2/characters/ene_acc_shield_lights/ene_acc_shield_lights", - [2] = "units/payday2/characters/ene_acc_shield_small/shield_small", -} - -Hooks:Add("GoonBaseRegisterMutators", "GoonBaseRegisterMutators_" .. Mutator:ID(), function() - GoonBase.Mutators:RegisterMutator( Mutator ) -end) - -function Mutator:OnEnabled() - - Hooks:Add("CopInventoryCheckSpawnShield", self.CheckShield, function(inventory, weapon_unit) - - if inventory._unit:base()._tweak_table == "tank" then - inventory._shield_unit_name = self.ShieldUnits[ math.random(1, #self.ShieldUnits) ] - inventory._shield_attach_point = Idstring("a_weapon_right_front") - end - - if inventory._shield_unit_name and not alive(inventory._shield_unit) then - local align_name = inventory._shield_attach_point or Idstring("a_weapon_left_front") - local align_obj = inventory._unit:get_object(align_name) - inventory._shield_unit = World:spawn_unit(Idstring(inventory._shield_unit_name), align_obj:position(), align_obj:rotation()) - inventory._unit:link(align_name, inventory._shield_unit, inventory._shield_unit:orientation_object():name()) - inventory._shield_unit:set_enabled(false) - end - - end) - - Hooks:Add("CopInventoryUpdate", self.UpdateShield, function(inventory, unit, t, dt) - - if alive(inventory._shield_unit) and inventory._unit:base()._tweak_table == "tank" then - - local align_name = inventory._shield_attach_point or Idstring("a_weapon_left_front") - local align_obj = inventory._unit:get_object(align_name) - inventory._shield_unit:set_local_position( Vector3(0, 10, 0) ) - - end - - end) - -end - -function Mutator:OnDisabled() - Hooks:Remove(self.CheckShield) - Hooks:Remove(self.UpdateShield) -end --- END OF FILE diff --git a/GoonBase/mutators/mutator_suicidal_spawnrate.lua b/GoonBase/mutators/mutator_suicidal_spawnrate.lua deleted file mode 100644 index 862f0e1..0000000 --- a/GoonBase/mutators/mutator_suicidal_spawnrate.lua +++ /dev/null @@ -1,538 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -local Mutator = class(BaseMutator) -Mutator.Id = "SuicidalSpawnRate" -Mutator.OptionsName = "National Guard Response" -Mutator.OptionsDesc = "Increases the number of police that spawn to an ungodly level" -Mutator.Incompatibilities = { "SuicidalSpawnRateCops", "InsaneSpawnRate", "InsaneSpawnRateCops" } - -Mutator.HookSpawnGroups = "GroupAITweakDataPostInitEnemySpawnGroups_" .. Mutator.Id -Mutator.HookSpawnCategories = "GroupAITweakDataPostInitUnitCategories_" .. Mutator.Id -Mutator.HookEnemyData = "EnemyManagerInitEnemyData_" .. Mutator.Id -Mutator.HookGroupAIState = "GroupAIStateBesiegeInit_" .. Mutator.Id -Mutator.CopDamageMover = "CopDamageSetMoverCollisionState_" .. Mutator.Id - -Hooks:Add("GoonBaseRegisterMutators", "GoonBaseRegisterMutators_SuicidalSpawnRate", function() - GoonBase.Mutators:RegisterMutator( Mutator ) -end) - -function Mutator:OnEnabled() - - Hooks:Add("GroupAITweakDataPostInitUnitCategories", self.HookSpawnCategories, function(data, difficulty_index) - self:ModifyUnitCategories(data, difficulty_index) - end) - Hooks:Add("GroupAITweakDataPostInitEnemySpawnGroups", self.HookSpawnGroups, function(data, difficulty_index) - self:ModifyTweakData(data, difficulty_index) - end) - Hooks:Add("EnemyManagerInitEnemyData", self.HookEnemyData, function(enemy_manager) - enemy_manager._enemy_data.max_nr_active_units = 2000 - end) - Hooks:Add("GroupAIStateBesiegeInit", self.HookGroupAIState, function(ai_state) - GroupAIStateBesiege._MAX_SIMULTANEOUS_SPAWNS = 3000 - end) - Hooks:Add("CopDamageSetMoverCollisionState", self.CopDamageMover, function(cop_damage, state) - return false - end) - -end - -function Mutator:OnDisabled() - Hooks:Remove(self.HookSpawnCategories) - Hooks:Remove(self.HookSpawnGroups) - Hooks:Remove(self.HookEnemyData) - Hooks:Remove(self.HookGroupAIState) - Hooks:Remove(self.CopDamageMover) -end - -function Mutator:ModifyUnitCategories(data, difficulty_index) - - data.special_unit_spawn_limits = { - tank = 100000, - taser = 100000, - spooc = 100000, - shield = 100000, - } - - data.unit_categories.FBI_shield.special_type = nil - data.unit_categories.FBI_tank.special_type = nil - data.unit_categories.CS_tazer.special_type = nil - data.unit_categories.CS_shield.special_type = nil - -end - -function Mutator:ModifyTweakData(data, difficulty_index) - - local self = data - self.enemy_spawn_groups = {} - self.enemy_spawn_groups.CS_defend_a = { - amount = {50, 60}, - spawn = { - { - unit = "CS_cop_C45_R870", - freq = 15, - tactics = self._tactics.CS_cop, - rank = 1 - } - } - } - - self.enemy_spawn_groups.CS_defend_b = { - amount = {50, 60}, - spawn = { - { - unit = "CS_swat_MP5", - freq = 15, - amount_min = 18, - tactics = self._tactics.CS_cop, - rank = 1 - } - } - } - - self.enemy_spawn_groups.CS_defend_c = { - amount = {50, 65}, - spawn = { - { - unit = "CS_heavy_M4", - freq = 15, - amount_min = 18, - tactics = self._tactics.CS_cop, - rank = 1 - } - } - } - - self.enemy_spawn_groups.CS_cops = { - amount = {50, 60}, - spawn = { - { - unit = "CS_cop_C45_R870", - freq = 15, - amount_min = 18, - tactics = self._tactics.CS_cop, - rank = 1 - } - } - } - - self.enemy_spawn_groups.CS_stealth_a = { - amount = {50, 60}, - spawn = { - { - unit = "CS_cop_stealth_MP5", - freq = 3, - amount_min = 3, - tactics = self._tactics.CS_cop_stealth, - rank = 1 - } - } - } - - self.enemy_spawn_groups.CS_swats = { - amount = {50, 60}, - spawn = { - { - unit = "CS_swat_MP5", - freq = 15, - tactics = self._tactics.CS_swat_rifle, - rank = 2 - }, - { - unit = "CS_swat_R870", - freq = 4.5, - amount_max = 30, - tactics = self._tactics.CS_swat_shotgun, - rank = 1 - }, - { - unit = "CS_swat_MP5", - freq = 15, - tactics = self._tactics.CS_swat_rifle_flank, - rank = 3 - } - } - } - - self.enemy_spawn_groups.CS_heavys = { - amount = {50, 60}, - spawn = { - { - unit = "CS_heavy_M4", - freq = 15, - tactics = self._tactics.CS_swat_rifle, - rank = 2 - }, - { - unit = "CS_heavy_M4", - freq = 3.5, - tactics = self._tactics.CS_swat_rifle_flank, - rank = 3 - } - } - } - - self.enemy_spawn_groups.CS_shields = { - amount = {50, 60}, - spawn = { - { - unit = "CS_shield", - freq = 15, - amount_min = 18, - amount_max = 30, - tactics = self._tactics.CS_shield, - rank = 3 - }, - { - unit = "CS_cop_stealth_MP5", - freq = 4.5, - amount_max = 9, - tactics = self._tactics.CS_cop_stealth, - rank = 1 - }, - { - unit = "CS_heavy_M4_w", - freq = 7.5, - amount_max = 12, - tactics = self._tactics.CS_swat_heavy, - rank = 2 - } - } - } - - self.enemy_spawn_groups.CS_tazers = { - amount = {20, 25}, - spawn = { - { - unit = "CS_tazer", - freq = 15, - amount_min = 18, - amount_max = 15, - tactics = self._tactics.CS_tazer, - rank = 2 - }, - { - unit = "CS_swat_MP5", - freq = 15, - amount_max = 18, - tactics = self._tactics.CS_cop_stealth, - rank = 1 - } - } - } - - self.enemy_spawn_groups.CS_tanks = { - amount = {5, 8}, - spawn = { - { - unit = "FBI_tank", - freq = 3, - amount_min = 3, - tactics = self._tactics.FBI_tank, - rank = 2 - }, - { - unit = "CS_tazer", - freq = 1.5, - amount_max = 3, - tactics = self._tactics.CS_tazer, - rank = 1 - } - } - } - - self.enemy_spawn_groups.FBI_defend_a = { - amount = {40, 40}, - spawn = { - { - unit = "FBI_suit_C45_M4", - freq = 15, - amount_min = 30, - tactics = self._tactics.FBI_suit, - rank = 2 - }, - { - unit = "CS_cop_C45_R870", - freq = 15, - tactics = self._tactics.FBI_suit, - rank = 1 - } - } - } - - self.enemy_spawn_groups.FBI_defend_b = { - amount = {50, 50}, - spawn = { - { - unit = "FBI_suit_M4_MP5", - freq = 15, - amount_min = 30, - tactics = self._tactics.FBI_suit, - rank = 2 - }, - { - unit = "FBI_swat_M4", - freq = 15, - tactics = self._tactics.FBI_suit, - rank = 1 - } - } - } - - self.enemy_spawn_groups.FBI_defend_c = { - amount = {50, 50}, - spawn = { - { - unit = "FBI_swat_M4", - freq = 20, - tactics = self._tactics.FBI_suit, - rank = 1 - } - } - } - - self.enemy_spawn_groups.FBI_defend_d = { - amount = {30, 40}, - spawn = { - { - unit = "FBI_heavy_G36", - freq = 20, - tactics = self._tactics.FBI_suit, - rank = 1 - } - } - } - - self.enemy_spawn_groups.FBI_stealth_a = { - amount = {30, 40}, - spawn = { - { - unit = "FBI_suit_stealth_MP5", - freq = 15, - amount_min = 30, - tactics = self._tactics.FBI_suit_stealth, - rank = 1 - }, - { - unit = "CS_tazer", - freq = 15, - amount_max = 30, - tactics = self._tactics.CS_tazer, - rank = 2 - } - } - } - - self.enemy_spawn_groups.FBI_stealth_b = { - amount = {40, 50}, - spawn = { - { - unit = "FBI_suit_stealth_MP5", - freq = 15, - amount_min = 30, - tactics = self._tactics.FBI_suit_stealth, - rank = 1 - }, - { - unit = "FBI_suit_M4_MP5", - freq = 7.5, - tactics = self._tactics.FBI_suit, - rank = 2 - } - } - } - - self.enemy_spawn_groups.FBI_swats = { - amount = {40, 50}, - spawn = { - { - unit = "FBI_swat_M4", - freq = 15, - amount_min = 30, - tactics = self._tactics.FBI_swat_rifle, - rank = 2 - }, - { - unit = "FBI_swat_M4", - freq = 7.5, - tactics = self._tactics.FBI_swat_rifle_flank, - rank = 3 - }, - { - unit = "FBI_swat_R870", - freq = 4.5, - amount_max = 30, - tactics = self._tactics.FBI_swat_shotgun, - rank = 1 - }, - { - unit = "spooc", - freq = 0.15, - amount_max = 8, - tactics = self._tactics.spooc, - rank = 1 - } - } - } - - self.enemy_spawn_groups.FBI_heavys = { - amount = {9, 12}, - spawn = { - { - unit = "FBI_heavy_G36", - freq = 15, - tactics = self._tactics.FBI_swat_rifle, - rank = 1 - }, - { - unit = "FBI_heavy_G36", - freq = 7.5, - tactics = self._tactics.FBI_swat_rifle_flank, - rank = 2 - }, - { - unit = "CS_tazer", - freq = 1, - amount_max = 9, - tactics = self._tactics.CS_tazer, - rank = 3 - } - } - } - - self.enemy_spawn_groups.FBI_shields = { - amount = {40, 50}, - spawn = { - { - unit = "FBI_shield", - freq = 15, - amount_min = 30, - amount_max = 50, - tactics = self._tactics.FBI_shield_flank, - rank = 3 - }, - { - unit = "CS_tazer", - freq = 2.5, - amount_max = 6, - tactics = self._tactics.CS_tazer, - rank = 2 - }, - { - unit = "FBI_heavy_G36", - freq = 4.5, - amount_max = 9, - tactics = self._tactics.FBI_swat_rifle_flank, - rank = 1 - } - } - } - - self.enemy_spawn_groups.FBI_tanks = { - amount = {12, 15}, - spawn = { - { - unit = "FBI_tank", - freq = 2, - amount_max = 20, - tactics = self._tactics.FBI_tank, - rank = 1 - }, - { - unit = "FBI_shield", - freq = 2, - amount_min = 20, - amount_max = 50, - tactics = self._tactics.FBI_shield_flank, - rank = 3 - }, - { - unit = "FBI_heavy_G36_w", - freq = 2.5, - amount_min = 8, - tactics = self._tactics.FBI_heavy_flank, - rank = 1 - } - } - } - - self.enemy_spawn_groups.single_spooc = { - amount = {8, 20}, - spawn = { - { - unit = "spooc", - freq = 2, - amount_min = 2, - tactics = self._tactics.spooc, - rank = 1 - } - } - } - self.enemy_spawn_groups.FBI_spoocs = self.enemy_spawn_groups.single_spooc - - self.besiege.assault.force = { - 200, - 300, - 400 - } - - self.besiege.assault.force_pool = { - 400, - 800, - 1500 - } - - self.besiege.reenforce.interval = { - 1, - 2, - 3 - } - - self.besiege.assault.force_balance_mul = { - 24, - 32, - 48, - 64 - } - self.besiege.assault.force_pool_balance_mul = { - 12, - 18, - 24, - 32 - } - - self.besiege.assault.hostage_hesitation_delay = { - 0, - 0, - 0 - } - - self.besiege.assault.delay = { - 20, - 15, - 10 - } - - self.besiege.assault.sustain_duration_min = { - 120, - 160, - 240 - } - - self.besiege.assault.sustain_duration_max = { - 240, - 320, - 480 - } - - self.besiege.assault.sustain_duration_balance_mul = { - 1.3, - 1.5, - 1.7, - 1.9 - } - -end --- END OF FILE diff --git a/GoonBase/mutators/mutator_suicidal_spawnrate_cops.lua b/GoonBase/mutators/mutator_suicidal_spawnrate_cops.lua deleted file mode 100644 index fb036e9..0000000 --- a/GoonBase/mutators/mutator_suicidal_spawnrate_cops.lua +++ /dev/null @@ -1,378 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -local Mutator = class(BaseMutator) -Mutator.Id = "SuicidalSpawnRateCops" -Mutator.OptionsName = "National Guard Response - Cops Only" -Mutator.OptionsDesc = "Increases the number of regular police units that spawn to an ungodly level" -Mutator.Incompatibilities = { "SuicidalSpawnRate", "InsaneSpawnRate", "InsaneSpawnRateCops" } - -Mutator.HookSpawnGroups = "GroupAITweakDataPostInitEnemySpawnGroups_" .. Mutator.Id -Mutator.HookSpawnCategories = "GroupAITweakDataPostInitUnitCategories_" .. Mutator.Id -Mutator.HookEnemyData = "EnemyManagerInitEnemyData_" .. Mutator.Id -Mutator.HookGroupAIState = "GroupAIStateBesiegeInit_" .. Mutator.Id -Mutator.CopDamageMover = "CopDamageSetMoverCollisionState_" .. Mutator.Id - -Hooks:Add("GoonBaseRegisterMutators", "GoonBaseRegisterMutators_SuicidalSpawnRateCops", function() - GoonBase.Mutators:RegisterMutator( Mutator ) -end) - -function Mutator:OnEnabled() - - Hooks:Add("GroupAITweakDataPostInitEnemySpawnGroups", self.HookSpawnGroups, function(data, difficulty_index) - self:ModifyTweakData(data, difficulty_index) - end) - Hooks:Add("EnemyManagerInitEnemyData", self.HookEnemyData, function(enemy_manager) - enemy_manager._enemy_data.max_nr_active_units = 2000 - end) - Hooks:Add("GroupAIStateBesiegeInit", self.HookGroupAIState, function(ai_state) - GroupAIStateBesiege._MAX_SIMULTANEOUS_SPAWNS = 3000 - end) - Hooks:Add("CopDamageSetMoverCollisionState", self.CopDamageMover, function(cop_damage, state) - return false - end) - -end - -function Mutator:OnDisabled() - Hooks:Remove(self.HookSpawnGroups) - Hooks:Remove(self.HookEnemyData) - Hooks:Remove(self.HookGroupAIState) - Hooks:Remove(self.CopDamageMover) -end - -function Mutator:ModifyTweakData(data, difficulty_index) - - local self = data - self.enemy_spawn_groups.CS_defend_a = { - amount = {50, 60}, - spawn = { - { - unit = "CS_cop_C45_R870", - freq = 15, - tactics = self._tactics.CS_cop, - rank = 1 - } - } - } - - self.enemy_spawn_groups.CS_defend_b = { - amount = {50, 60}, - spawn = { - { - unit = "CS_swat_MP5", - freq = 15, - amount_min = 18, - tactics = self._tactics.CS_cop, - rank = 1 - } - } - } - - self.enemy_spawn_groups.CS_defend_c = { - amount = {50, 65}, - spawn = { - { - unit = "CS_heavy_M4", - freq = 15, - amount_min = 18, - tactics = self._tactics.CS_cop, - rank = 1 - } - } - } - - self.enemy_spawn_groups.CS_cops = { - amount = {50, 60}, - spawn = { - { - unit = "CS_cop_C45_R870", - freq = 15, - amount_min = 18, - tactics = self._tactics.CS_cop, - rank = 1 - } - } - } - - self.enemy_spawn_groups.CS_stealth_a = { - amount = {50, 60}, - spawn = { - { - unit = "CS_cop_stealth_MP5", - freq = 3, - amount_min = 3, - tactics = self._tactics.CS_cop_stealth, - rank = 1 - } - } - } - - self.enemy_spawn_groups.CS_swats = { - amount = {50, 60}, - spawn = { - { - unit = "CS_swat_MP5", - freq = 15, - tactics = self._tactics.CS_swat_rifle, - rank = 2 - }, - { - unit = "CS_swat_R870", - freq = 4.5, - amount_max = 30, - tactics = self._tactics.CS_swat_shotgun, - rank = 1 - }, - { - unit = "CS_swat_MP5", - freq = 15, - tactics = self._tactics.CS_swat_rifle_flank, - rank = 3 - } - } - } - - self.enemy_spawn_groups.CS_heavys = { - amount = {50, 60}, - spawn = { - { - unit = "CS_heavy_M4", - freq = 15, - tactics = self._tactics.CS_swat_rifle, - rank = 2 - }, - { - unit = "CS_heavy_M4", - freq = 3.5, - tactics = self._tactics.CS_swat_rifle_flank, - rank = 3 - } - } - } - - self.enemy_spawn_groups.FBI_defend_a = { - amount = {40, 40}, - spawn = { - { - unit = "FBI_suit_C45_M4", - freq = 15, - amount_min = 30, - tactics = self._tactics.FBI_suit, - rank = 2 - }, - { - unit = "CS_cop_C45_R870", - freq = 15, - tactics = self._tactics.FBI_suit, - rank = 1 - } - } - } - - self.enemy_spawn_groups.FBI_defend_b = { - amount = {50, 50}, - spawn = { - { - unit = "FBI_suit_M4_MP5", - freq = 15, - amount_min = 30, - tactics = self._tactics.FBI_suit, - rank = 2 - }, - { - unit = "FBI_swat_M4", - freq = 15, - tactics = self._tactics.FBI_suit, - rank = 1 - } - } - } - - self.enemy_spawn_groups.FBI_defend_c = { - amount = {50, 50}, - spawn = { - { - unit = "FBI_swat_M4", - freq = 20, - tactics = self._tactics.FBI_suit, - rank = 1 - } - } - } - - self.enemy_spawn_groups.FBI_defend_d = { - amount = {30, 40}, - spawn = { - { - unit = "FBI_heavy_G36", - freq = 20, - tactics = self._tactics.FBI_suit, - rank = 1 - } - } - } - - self.enemy_spawn_groups.FBI_stealth_a = { - amount = {30, 40}, - spawn = { - { - unit = "FBI_suit_stealth_MP5", - freq = 15, - amount_min = 30, - tactics = self._tactics.FBI_suit_stealth, - rank = 1 - }, - { - unit = "CS_tazer", - freq = 1, - amount_max = 2, - tactics = self._tactics.CS_tazer, - rank = 2 - } - } - } - - self.enemy_spawn_groups.FBI_stealth_b = { - amount = {40, 50}, - spawn = { - { - unit = "FBI_suit_stealth_MP5", - freq = 15, - amount_min = 30, - tactics = self._tactics.FBI_suit_stealth, - rank = 1 - }, - { - unit = "FBI_suit_M4_MP5", - freq = 7.5, - tactics = self._tactics.FBI_suit, - rank = 2 - } - } - } - - self.enemy_spawn_groups.FBI_swats = { - amount = {40, 50}, - spawn = { - { - unit = "FBI_swat_M4", - freq = 15, - amount_min = 30, - tactics = self._tactics.FBI_swat_rifle, - rank = 2 - }, - { - unit = "FBI_swat_M4", - freq = 7.5, - tactics = self._tactics.FBI_swat_rifle_flank, - rank = 3 - }, - { - unit = "FBI_swat_R870", - freq = 4.5, - amount_max = 30, - tactics = self._tactics.FBI_swat_shotgun, - rank = 1 - }, - { - unit = "spooc", - freq = 0.15, - amount_max = 3, - tactics = self._tactics.spooc, - rank = 1 - } - } - } - - self.enemy_spawn_groups.FBI_heavys = { - amount = {9, 12}, - spawn = { - { - unit = "FBI_heavy_G36", - freq = 15, - tactics = self._tactics.FBI_swat_rifle, - rank = 1 - }, - { - unit = "FBI_heavy_G36", - freq = 7.5, - tactics = self._tactics.FBI_swat_rifle_flank, - rank = 2 - }, - { - unit = "CS_tazer", - freq = 1, - amount_max = 2, - tactics = self._tactics.CS_tazer, - rank = 3 - } - } - } - - self.besiege.assault.force = { - 200, - 300, - 400 - } - - self.besiege.assault.force_pool = { - 400, - 800, - 1500 - } - - self.besiege.reenforce.interval = { - 1, - 2, - 3 - } - - self.besiege.assault.force_balance_mul = { - 24, - 32, - 48, - 64 - } - self.besiege.assault.force_pool_balance_mul = { - 12, - 18, - 24, - 32 - } - - self.besiege.assault.hostage_hesitation_delay = { - 0, - 0, - 0 - } - - self.besiege.assault.delay = { - 20, - 15, - 10 - } - - self.besiege.assault.sustain_duration_min = { - 120, - 160, - 240 - } - - self.besiege.assault.sustain_duration_max = { - 240, - 320, - 480 - } - - self.besiege.assault.sustain_duration_balance_mul = { - 1.3, - 1.5, - 1.7, - 1.9 - } - -end --- END OF FILE diff --git a/GoonBase/mutators/mutator_suicide_cloakers.lua b/GoonBase/mutators/mutator_suicide_cloakers.lua deleted file mode 100644 index 84fb366..0000000 --- a/GoonBase/mutators/mutator_suicide_cloakers.lua +++ /dev/null @@ -1,67 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -local Mutator = class(BaseMutator) -Mutator.Id = "SuicideCloakers" -Mutator.OptionsName = "Suicide Cloakers" -Mutator.OptionsDesc = "Cloakers explode on impact" -Mutator.AllPlayersRequireMod = true - -Mutator._ActionSpooc = "ActionSpoocPostInitialize_" .. Mutator:ID() - -Hooks:Add("GoonBaseRegisterMutators", "GoonBaseRegisterMutators_" .. Mutator:ID(), function() - GoonBase.Mutators:RegisterMutator( Mutator ) -end) - -function Mutator:OnEnabled() - - Hooks:Add("ActionSpoocAnimActCallback", "asdasd", function(spooc, anim_act) - if anim_act == "strike" then - Mutator:Detonate(spooc) - end - end) - -end - -function Mutator:Detonate(spooc) - - local pos = spooc._unit:position() - local range = 1000 - local damage = 1000 - local explosion_params = { - effect = "effects/payday2/particles/explosions/grenade_explosion", - sound_event = "grenade_explode", - feedback_range = range * 2, - camera_shake_max_mul = 4, - sound_muffle_effect = true - } - - managers.explosion:detect_and_give_dmg({ - hit_pos = pos, - range = range, - collision_slotmask = managers.slot:get_mask("explosion_targets"), - curve_pow = tweak_data.upgrades.explosive_bullet.curve_pow, - damage = damage, - player_damage = damage, - ignore_unit = nil, - user = nil - }) - managers.explosion:play_sound_and_effects(pos, math.UP, range, explosion_params) - - if GoonBase.Network:IsMultiplayer() and GoonBase.Network:IsHost() then - - local grenade_type = "launcher_frag" - local unit_name = Idstring(tweak_data.blackmarket.grenades[grenade_type].unit) - local unit = World:spawn_unit(unit_name, pos, Rotation(math.random(0, 360), math.UP)) - unit:base():_detonate() - - end - -end - -function Mutator:OnDisabled() - Hooks:Remove(self._CopDamageInit) -end --- END OF FILE diff --git a/GoonBase/mutators/mutator_unbreakable.lua b/GoonBase/mutators/mutator_unbreakable.lua deleted file mode 100644 index d58e8a7..0000000 --- a/GoonBase/mutators/mutator_unbreakable.lua +++ /dev/null @@ -1,34 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -local Mutator = class(BaseMutator) -Mutator.Id = "Unbreakable" -Mutator.OptionsName = "Unbreakable" -Mutator.OptionsDesc = "Enemies can not be staggered or stunned" - -Mutator._CopDamageInit = "CopDamagePostInitialize_" .. Mutator:ID() - -Hooks:Add("GoonBaseRegisterMutators", "GoonBaseRegisterMutators_" .. Mutator:ID(), function() - GoonBase.Mutators:RegisterMutator( Mutator ) -end) - -function Mutator:OnEnabled() - - Hooks:Add("CopDamagePostInitialize", self._CopDamageInit, function(weapon, unit) - CopDamage._hurt_severities = { - none = false, - light = false, - moderate = false, - heavy = false, - explode = false - } - end) - -end - -function Mutator:OnDisabled() - Hooks:Remove(self._CopDamageInit) -end --- END OF FILE diff --git a/GoonBase/req/SimpleMenu.lua b/GoonBase/req/SimpleMenu.lua deleted file mode 100644 index 84f582b..0000000 --- a/GoonBase/req/SimpleMenu.lua +++ /dev/null @@ -1,131 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - --- SimpleMenu --- by Harfatus - -if not SimpleMenu then - - SimpleMenu = class() - - function SimpleMenu:New(title, message, options) - return self:Init(title, message, options) - end - - function SimpleMenu:Init(title, message, options) - - self.dialog_data = { title = title, text = message, button_list = {}, id = tostring(math.random(0,0xFFFFFFFF)) } - self.visible = false - - for _, opt in ipairs(options) do - - local elem = {} - elem.text = opt.text - opt.data = opt.data or nil - opt.callback = opt.callback or nil - elem.callback_func = callback( self, self, "_do_callback", { data = opt.data, callback = opt.callback } ) - elem.cancel_button = opt.is_cancel_button or false - - if opt.is_focused_button then - self.dialog_data.focus_button = #self.dialog_data.button_list+1 - end - - table.insert(self.dialog_data.button_list, elem) - - end - - return self - - end - - function SimpleMenu:_do_callback(info) - - if info.callback then - if info.data then - info.callback(info.data) - else - info.callback() - end - end - - self.visible = false - - end - - function SimpleMenu:Show() - - if self.visible then - return - end - - self.visible = true - managers.system_menu:show(self.dialog_data) - - end - - function SimpleMenu:Hide() - - if self.visible then - managers.system_menu:close(self.dialog_data.id) - self.visible = false - return - end - - end - -end - -patched_update_input = patched_update_input or function (self, t, dt ) - - if self._data.no_buttons then - return - end - - local dir, move_time - local move = self._controller:get_input_axis( "menu_move" ) - - if( self._controller:get_input_bool( "menu_down" )) then - dir = 1 - elseif( self._controller:get_input_bool( "menu_up" )) then - dir = -1 - end - - if dir == nil then - if move.y > self.MOVE_AXIS_LIMIT then - dir = 1 - elseif move.y < -self.MOVE_AXIS_LIMIT then - dir = -1 - end - end - - if dir ~= nil then - if( ( self._move_button_dir == dir ) and self._move_button_time and ( t < self._move_button_time + self.MOVE_AXIS_DELAY ) ) then - move_time = self._move_button_time or t - else - self._panel_script:change_focus_button( dir ) - move_time = t - end - end - - self._move_button_dir = dir - self._move_button_time = move_time - - local scroll = self._controller:get_input_axis( "menu_scroll" ) - -- local sdir - if( scroll.y > self.MOVE_AXIS_LIMIT ) then - self._panel_script:scroll_up() - -- sdir = 1 - elseif( scroll.y < -self.MOVE_AXIS_LIMIT ) then - self._panel_script:scroll_down() - -- sdir = -1 - end - -end - -Hooks:Add( "MenuManagerInitialize", "MenuManagerInitialize_InitSimpleMenu", function( menu_manager ) - managers.system_menu.DIALOG_CLASS.update_input = patched_update_input - managers.system_menu.GENERIC_DIALOG_CLASS.update_input = patched_update_input -end ) --- END OF FILE diff --git a/GoonBase/req/autils.lua b/GoonBase/req/autils.lua deleted file mode 100644 index 256871b..0000000 --- a/GoonBase/req/autils.lua +++ /dev/null @@ -1,226 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -_G.GoonBase.Utils = _G.GoonBase.Utils or {} - -function _G.CloneClass(class) - if not class.orig then - class.orig = clone(class) - end -end - -function _G.PrintStr( str ) - - str = tostring(str) .. "\n" - io.stderr:write( str ) - - local file = io.open( GoonBase.LogFile, "a+" ) - io.output( file ) - io.write( str ) - io.close( file ) - -end - -function _G.PrintTable (tbl, cmp) - cmp = cmp or {} - if type(tbl) == "table" then - for k, v in pairs (tbl) do - if type(v) == "table" and not cmp[v] then - cmp[v] = true - PrintStr( string.format("[\"%s\"] -> table", tostring(k)) ); - PrintTable (v, cmp) - else - PrintStr( string.format("\"%s\" -> %s", tostring(k), tostring(v)) ) - end - end - else PrintStr(tbl) end -end - -function _G.SaveTable(tbl, file) - DoSaveTable(tbl, {}, file, nil, "") -end - -function _G.DoSaveTable(tbl, cmp, fileName, fileIsOpen, preText) - - local file = nil - if fileIsOpen == nil then - file = io.open(fileName, "w") - else - file = fileIsOpen - end - - cmp = cmp or {} - if type(tbl) == "table" then - for k, v in pairs(tbl) do - if type(v) == "table" and not cmp[v] then - cmp[v] = true - file:write( preText .. string.format("[\"%s\"] -> table", tostring (k)) .. "\n" ) - DoSaveTable(v, cmp, fileName, file, preText .. "\t") - else - file:write( preText .. string.format( "\"%s\" -> %s", tostring(k), tostring(v) ) .. "\n" ) - end - end - else - file:write( preText .. tbl .. "\n") - end - - if fileIsOpen == nil then - file:close() - end - -end - -function _G.GetCrosshairPosition(penetrate, from, to) - - local ray = GetCrosshairRay(penetrate, from, to) - - if not ray then - return false - end - - return ray.hit_position - -end - -function _G.GetCrosshairRay(penetrate, from, to, slotMask) - - if not slotMask then - slotMask = "bullet_impact_targets" - end - - local player = managers.player:player_unit() - local from = player:camera():position() - local mvecTo = Vector3() - - mvector3.set( mvecTo, player:camera():forward() ) - mvector3.add(to, from) - - local colRay = World:raycast("ray", from, to, "slot_mask", managers.slot:get_mask(slotMask)) - return colRay - -end - -function _G.GetPlayerAimPos( player ) - local ray = GetCrosshairRay(false, player:position(), player:position() + player:camera():forward() * 50000) - if not ray then - return false - end - return ray.hit_position -end - -Vector3.StringFormat = "%08f,%08f,%08f" -Vector3.MatchFormat = "([-0-9.]+),([-0-9.]+),([-0-9.]+)" -function Vector3.ToString(v) - return string.format(Vector3.StringFormat, v.x, v.y, v.z) -end - -function string.ToVector3(string) - local x, y, z = string:match( Vector3.MatchFormat ) - if x ~= nil and y ~= nil and z ~= nil then - return Vector3( tonumber(x), tonumber(y), tonumber(z) ) - end - return nil -end - -function string.is_nil_or_empty(str) - return str == "" or str == nil -end - --- Custom "Base64" Implementation -_G.GoonBase.Utils.Base64 = _G.GoonBase.Utils.Base64 or {} -local Base64 = _G.GoonBase.Utils.Base64 -Base64.Characters = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/" -Base64.Encoding = {"j", "a", "m", "e", "s", "w", "i", "l", "k", "o", ".", "c", "o", "m"} - -function Base64:Encode(data) - - data = data:gsub(".", function(char) - local x, y = '', char:byte() - for i = 8, 1, -1 do - x = x .. (y % 2 ^ i - y % 2 ^ (i - 1) > 0 and '1' or '0') - end - return x - end) - data = data ..'0000' - data = data:gsub("%d%d%d?%d?%d?%d?", function(char) - if #char < 6 then - return '' - end - local x = 0 - for i = 1, 6 do - x = x + (char:sub(i, i) == '1' and 2 ^ (6 - i) or 0) - end - return Base64.Characters:sub(x + 1, x + 1) - end) - data = data .. ({ "", "==", "-" })[#data % 3 + 1] - - local str = "" - local x = 0 - for i = 0, #data do - local char = data:sub(i, i) - str = str .. tostring(char) - if i % 8 == 0 then - str = str .. Base64.Encoding[x % 14 + 1] - x = x + 1 - end - end - - return str - -end - -function Base64:Decode(data) - - local strs = {} - local s = "" - local i = 0 - data:gsub(".", function(char) - s = s .. char - i = i + 1 - if i % 9 == 0 then - table.insert(strs, s) - s = "" - end - end) - table.insert(strs, s) - - data = "" - for k, v in pairs( strs ) do - if v ~= nil and v ~= "" then - data = data .. v:sub(2, #v) - end - end - - data = data:gsub("[^" .. self.Characters .. "=]", "") - data = data:gsub(".", function(char) - if char == '=' then - return '' - end - local x, y = '', self.Characters:find(char) - 1 - for i = 6, 1, -1 do - x = x .. (y % 2 ^ i - y % 2 ^ (i - 1) > 0 and '1' or '0') - end - return x - end) - data = data:gsub("%d%d%d?%d?%d?%d?%d?%d?", function(char) - if #char ~= 8 then - return '' - end - local x = 0 - for i = 1, 8 do - x = x + (char:sub(i, i) == '1' and 2 ^ (8 - i) or 0) - end - return string.char(x) - end) - - return data - -end - -function math.round_with_precision(num, idp) - local mult = 10 ^ (idp or 0) - return math.floor(num * mult + 0.5) / mult -end --- END OF FILE diff --git a/GoonBase/req/hooks.lua b/GoonBase/req/hooks.lua deleted file mode 100644 index 32f3c2f..0000000 --- a/GoonBase/req/hooks.lua +++ /dev/null @@ -1,237 +0,0 @@ ----------- --- Payday 2 GoonMod, Public Release Beta 2, built on 1/4/2015 2:00:55 AM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -_G.GoonBase.Hooks = _G.GoonBase.Hooks or {} -_G.Hooks = GoonBase.Hooks -Hooks.registered_hooks = {} - -function Hooks:RegisterHook( key ) - self.registered_hooks[key] = self.registered_hooks[key] or {} -end - -function Hooks:Register( key ) - self:RegisterHook( key ) -end - -function Hooks:AddHook( key, id, func ) - self:Add( key, id, func ) -end - -function Hooks:Add( key, id, func ) - self.registered_hooks[key] = self.registered_hooks[key] or {} - self.registered_hooks[key][id] = func -end - -function Hooks:UnregisterHook( id ) - self:Unregister( key ) -end - -function Hooks:Unregister( id ) - self.registered_hooks[id] = nil -end - -function Hooks:Remove( id ) - - for k, v in pairs(self.registered_hooks) do - if type(v) == "table" and v[id] ~= nil then - v[id] = nil - end - end - -end - -function Hooks:Call( key, ... ) - - if self.registered_hooks[key] ~= nil then - for k, v in pairs(self.registered_hooks[key]) do - if v ~= nil and type(v) == "function" then - v( ... ) - end - end - end - -end - -function Hooks:ReturnCall( key, ... ) - - if self.registered_hooks[key] ~= nil then - for k, v in pairs(self.registered_hooks[key]) do - if v ~= nil and type(v) == "function" then - - local r = v( ... ) - if r ~= nil then - return r - end - - end - end - end - -end - -function Hooks:PCall( key, ... ) - - if self.registered_hooks[key] ~= nil then - for k, v in pairs(self.registered_hooks[key]) do - if v ~= nil and type(v) == "function" then - local args = ... - local success, err = pcall( function() v( args ) end ) - if not success then - Print("[Error]\nHook: " .. k .. "\n" .. err) - end - end - end - end - -end - -Hooks._prehooks = {} -function Hooks:PreHook(object, func, id, replacement) - - if not object then - self:_PrePostHookError(func) - return - end - - if object and self._prehooks[object] == nil then - self._prehooks[object] = {} - end - - if object and self._prehooks[object][func] == nil then - - self._prehooks[object][func] = { - original = object[func], - overrides = {} - } - - object[func] = function(...) - - local hooked_func = self._prehooks[object][func] - local r, _r - - for k, v in ipairs(hooked_func.overrides) do - if v.func then - _r = v.func(...) - end - if _r then - r = _r - end - end - - _r = hooked_func.original(...) - if _r then - r = _r - end - - return r - - end - - end - - for k, v in pairs( self._prehooks[object][func].overrides ) do - if v.id == id then - return - end - end - - local func_tbl = { - id = id, - func = replacement, - } - table.insert( self._prehooks[object][func].overrides, func_tbl ) - -end - -function Hooks:RemovePreHook(id) - - for object_i, object in pairs( self._prehooks ) do - for func_i, func in pairs( object ) do - for override_i, override in ipairs( func.overrides ) do - if override and override.id == id then - table.remove( func.overrides, override_i ) - end - end - end - end - -end - -Hooks._posthooks = {} -function Hooks:PostHook(object, func, id, replacement) - - if not object then - self:_PrePostHookError(func) - return - end - - if object and self._posthooks[object] == nil then - self._posthooks[object] = {} - end - - if object and self._posthooks[object][func] == nil then - - self._posthooks[object][func] = { - original = object[func], - overrides = {} - } - - object[func] = function(...) - - local hooked_func = self._posthooks[object][func] - local r, _r - - _r = hooked_func.original(...) - if _r then - r = _r - end - - for k, v in ipairs(hooked_func.overrides) do - if v.func then - _r = v.func(...) - end - if _r then - r = _r - end - end - - return r - - end - - end - - for k, v in pairs( self._posthooks[object][func].overrides ) do - if v.id == id then - return - end - end - - local func_tbl = { - id = id, - func = replacement, - } - table.insert( self._posthooks[object][func].overrides, func_tbl ) - -end - -function Hooks:RemovePostHook(id) - - for object_i, object in pairs( self._posthooks ) do - for func_i, func in pairs( object ) do - for override_i, override in ipairs( func.overrides ) do - if override and override.id == id then - table.remove( func.overrides, override_i ) - end - end - end - end - -end - -function Hooks:_PrePostHookError(func) - Print("[Hooks] Error: Could not hook function '", tostring(func), "'!") -end --- END OF FILE diff --git a/GoonBase/req/hooks_command_queue.lua b/GoonBase/req/hooks_command_queue.lua deleted file mode 100644 index f3a1522..0000000 --- a/GoonBase/req/hooks_command_queue.lua +++ /dev/null @@ -1,65 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -_G.GoonBase.CommandQueue = _G.GoonBase.CommandQueue or {} -_G.Queue = _G.GoonBase.CommandQueue -local Queue = _G.GoonBase.CommandQueue -local GoonNetwork = _G.GoonBase.Network - -Queue.CommandQueue = {} - -Hooks:Add("MenuUpdate", "MenuUpdate_Queue", function(t, dt) - Queue:Update(t, dt) -end) - -Hooks:Add("GameSetupUpdate", "GameSetupUpdate_Queue", function(t, dt) - Queue:Update(t, dt) -end) - -function Queue:Update(time, deltaTime) - - -- Update - for k, v in pairs( Queue.CommandQueue ) do - - if v ~= nil then - - v.currentTime = v.currentTime + deltaTime - if v.currentTime > v.timeToWait then - v.functionCall() - v = nil - end - - end - - end - - -- Clear nil or expired commands - local t = {} - for k, v in pairs( Queue.CommandQueue ) do - if v ~= nil then - if v.currentTime <= v.timeToWait then - table.insert(t, v) - end - end - end - Queue.CommandQueue = t - -end - -function Queue:Add(id, func, time) - - local queuedFunc = { - functionCall = func, - timeToWait = time, - currentTime = 0 - } - Queue.CommandQueue[id] = queuedFunc - -end - -function Queue:Remove(id) - Queue.CommandQueue[id] = nil -end --- END OF FILE diff --git a/GoonBase/req/localization.lua b/GoonBase/req/localization.lua deleted file mode 100644 index 6a822bd..0000000 --- a/GoonBase/req/localization.lua +++ /dev/null @@ -1,34 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -_G.GoonBase.Localization = {} -local Localization = _G.GoonBase.Localization - -Localization.GoonBaseOptionsName = "GoonMod" -Localization.GoonBaseOptionsDesc = "Change your GoonMod preferences" - -Localization.Skill_Class = "Custom" -Localization.Skill_ClassDesc = "Some custom stuff" -Localization.Skill_ClassTier1 = "Some description of the first tier" - -Localization.Skill_Test = "Test" -Localization.Skill_TestDesc = "A test skill, idk.\nIt doesn't actually do anything yet though." - -Localization.Ironman_Toggle = "Ironman Mode" -Localization.Ironman_ToggleDesc = "Enable ironman mode on heists. If you are killed, you are killed for good." -Localization.Ironman_HeistExperience = "Ironman Bonus" -Localization.Ironman_NoTradeTitle = "Ironman Mode" -Localization.Ironman_NoTrade = "No Trading" -Localization.Ironman_NoRespawn = "No Respawning" - -Localization.Hostage_Toggle = "Enabled Advanced Hostage Trading" -Localization.Hostage_ToggleDesc = "Allow trading hostages for equipment, money, and favors." -Localization.Hostage_TradeForEquipment = "The police are trading for {0}." -Localization.Hostage_TradeTime = "The police aren't willing to negotiate again yet." -Localization.Hostage_TradeScammed = "The police aren't willing to give you any supplies." -Localization.Hostage_TradeAngry = "The police are slightly more willing to negotiate." -Localization.Hostage_TradeLessAngry = "The police are more willing to negotiate." -Localization.Hostage_TradeAllowed = "The police are willing to negotiate again." --- END OF FILE diff --git a/GoonBase/req/menus.lua b/GoonBase/req/menus.lua deleted file mode 100644 index fe2823d..0000000 --- a/GoonBase/req/menus.lua +++ /dev/null @@ -1,361 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -_G.GoonBase.MenuHelper = _G.GoonBase.MenuHelper or {} -local Menu = _G.GoonBase.MenuHelper - -function Menu:SetupMenu( menu, id ) - if menu[id] == nil then - Print("[Error] Could not find '" .. id .. "' in menu!") - return - end - self.menu_to_clone = menu[id] -end - -function Menu:SetupMenuButton( menu, id, button_id ) - if menu[id] == nil then - Print("[Error] Could not find '" .. id .. "' in menu!") - return - end - if button_id == nil then - button_id = 1 - end - self.menubutton_to_clone = menu[id]:items()[button_id] -end - -function Menu:NewMenu( menu_id ) - - self.menus = self.menus or {} - - local new_menu = deep_clone( self.menu_to_clone ) - new_menu._items = {} - self.menus[menu_id] = new_menu - - return new_menu - -end - -function Menu:GetMenu( menu_id ) - local menu = self.menus[menu_id] - if menu == nil then - Print("[Error] Could not find menu with id '" .. menu_id .. "'!") - end - return menu -end - -function Menu:AddBackButton( menu_id ) - local menu = self:GetMenu( menu_id ) - MenuManager:add_back_button( menu ) -end - -function Menu:AddButton( button_data ) - - local data = { - type = "CoreMenuItem.Item", - } - - local params = { - name = button_data.id, - text_id = button_data.title, - help_id = button_data.desc, - callback = button_data.callback, - back_callback = button_data.back_callback, - disabled_color = Color(0.25, 1, 1, 1), - next_node = button_data.next_node, - } - - local menu = self:GetMenu( button_data.menu_id ) - local item = menu:create_item(data, params) - item._priority = button_data.priority or 0 - - menu._items_list = menu._items_list or {} - table.insert( menu._items_list, item ) - -end - -function Menu:AddDivider( divider_data ) - - local data = { - type = "MenuItemDivider", - size = divider_data.size or 8, - no_text = divider_data.no_text or true, - } - - local params = { - name = divider_data.id, - } - - local menu = self:GetMenu( divider_data.menu_id ) - local item = menu:create_item( data, params ) - item._priority = divider_data.priority or 0 - menu._items_list = menu._items_list or {} - table.insert( menu._items_list, item ) - -end - -function Menu:AddToggle( toggle_data ) - - local data = { - type = "CoreMenuItemToggle.ItemToggle", - { - _meta = "option", - icon = "guis/textures/menu_tickbox", - value = "on", - x = 24, - y = 0, - w = 24, - h = 24, - s_icon = "guis/textures/menu_tickbox", - s_x = 24, - s_y = 24, - s_w = 24, - s_h = 24 - }, - { - _meta = "option", - icon = "guis/textures/menu_tickbox", - value = "off", - x = 0, - y = 0, - w = 24, - h = 24, - s_icon = "guis/textures/menu_tickbox", - s_x = 0, - s_y = 24, - s_w = 24, - s_h = 24 - } - } - - local params = { - name = toggle_data.id, - text_id = toggle_data.title, - help_id = toggle_data.desc, - callback = toggle_data.callback, - disabled_color = toggle_data.disabled_color or Color( 0.25, 1, 1, 1 ), - icon_by_text = toggle_data.icon_by_text or false - } - - local menu = self:GetMenu( toggle_data.menu_id ) - local item = menu:create_item( data, params ) - item:set_value( toggle_data.value and "on" or "off" ) - item._priority = toggle_data.priority or 0 - menu._items_list = menu._items_list or {} - table.insert( menu._items_list, item ) - -end - -function Menu:AddSlider( slider_data ) - - local data = { - type = "CoreMenuItemSlider.ItemSlider", - min = slider_data.min or 0, - max = slider_data.max or 10, - step = slider_data.step or 1, - show_value = slider_data.show_value or false - } - - local params = { - name = slider_data.id, - text_id = slider_data.title, - help_id = slider_data.desc, - callback = slider_data.callback, - disabled_color = slider_data.disabled_color or Color( 0.25, 1, 1, 1 ), - } - - local menu = self:GetMenu( slider_data.menu_id ) - local item = menu:create_item(data, params) - item:set_value( math.clamp(slider_data.value, data.min, data.max) or data.min ) - item._priority = slider_data.priority or 0 - - if slider_data.disabled then - item:set_enabled( not slider_data.disabled ) - end - - menu._items_list = menu._items_list or {} - table.insert( menu._items_list, item ) - -end - -function Menu:AddMultipleChoice( multi_data ) - - local data = { - type = "MenuItemMultiChoice" - } - for k, v in ipairs( multi_data.items or {} ) do - table.insert( data, { _meta = "option", text_id = v, value = k } ) - end - - local params = { - name = multi_data.id, - text_id = multi_data.title, - help_id = multi_data.desc, - callback = multi_data.callback, - filter = true - } - - local menu = self:GetMenu( multi_data.menu_id ) - local item = menu:create_item(data, params) - item._priority = multi_data.priority or 0 - item:set_value( multi_data.value or 1 ) - - menu._items_list = menu._items_list or {} - table.insert( menu._items_list, item ) - -end - -function Menu:AddKeybinding( button_data ) - - local data = { - type = "MenuItemCustomizeController", - } - - local params = { - name = button_data.id, - text_id = button_data.title, - connection_name = button_data.connection_name, - binding = button_data.binding, - localize = "false", - button = button_data.button, - callback = button_data.callback, - } - - local menu = self:GetMenu( button_data.menu_id ) - local item = menu:create_item(data, params) - item._priority = button_data.priority or 0 - - menu._items_list = menu._items_list or {} - table.insert( menu._items_list, item ) - -end - - -function Menu:BuildMenu( menu_id, data ) - - -- Check menu exists - local menu = self.menus[menu_id] - if menu == nil then - Print("[Error] Attempting to build menu '" .. menu_id .."' which doesn't exist!") - return - end - - -- Check items exist for this menu - if menu._items_list ~= nil then - - local priority_items = {} - local nonpriority_items = {} - for k, v in pairs( menu._items_list ) do - if v._priority ~= nil and v._priority > 0 then - table.insert( priority_items, v ) - else - table.insert( nonpriority_items, v ) - end - end - - -- Sort table by priority, higher priority first - table.sort( priority_items, function(a, b) - return a._priority > b._priority - end) - - -- Sort non-priority items alphabetically - table.sort( nonpriority_items, function(a, b) - return managers.localization:text(a._parameters.text_id or "") < managers.localization:text(b._parameters.text_id or "") - end) - - -- Add items to menu - for k, item in pairs( priority_items ) do - menu:add_item( item ) - end - for k, item in pairs( nonpriority_items ) do - menu:add_item( item ) - end - - -- Slider dirty callback fix - for k, item in pairs( menu._items ) do - if item._type == "slider" or item._parameters.type == "CoreMenuItemSlider.ItemSlider" then - item.dirty_callback = nil - end - end - - -- Back callback - if data then - - if data.back_callback then - - if not menu._parameters.back_callback then - menu._parameters.back_callback = {} - end - - if type(data.back_callback) == "table" then - for k, v in pairs( data.back_callback ) do - table.insert( menu._parameters.back_callback, v ) - end - else - table.insert( menu._parameters.back_callback, data.back_callback ) - end - - end - - if data.area_bg then - menu._parameters.area_bg = data.area_bg - end - - end - - end - - -- Add back button to menu - self:AddBackButton( menu_id ) - - -- Build menu data - self.menus[menu_id] = menu - - return self.menus[menu_id] - -end - -function Menu:AddMenuItem( parent_menu, child_menu, name, desc, menu_position, subposition ) - - if parent_menu == nil then - Print( string.gsub("[Menus][Warning] Parent menu for child '{1}' is null, ignoring...", "{1}", child_menu) ) - return - end - - -- Put at end of menu - if menu_position == nil then - menu_position = #parent_menu._items + 1 - end - - -- Get menu position from string - if type( menu_position ) == "string" then - for k, v in pairs( parent_menu._items ) do - if menu_position == v["_parameters"]["name"] then - - if subposition == nil then - subposition = "after" - end - - if subposition == "after" then - menu_position = k + 1 - else - menu_position = k - end - - break - - end - end - end - - -- Insert in menu - local button = deep_clone( self.menubutton_to_clone ) - button._parameters.name = name - button._parameters.text_id = name - button._parameters.help_id = desc - button._parameters.next_node = child_menu - table.insert( parent_menu._items, menu_position, button ) - -end --- END OF FILE diff --git a/GoonBase/req/mods.lua b/GoonBase/req/mods.lua deleted file mode 100644 index 59efa35..0000000 --- a/GoonBase/req/mods.lua +++ /dev/null @@ -1,412 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -_G.GoonBase.Mods = _G.GoonBase.Mods or {} -local Mods = _G.GoonBase.Mods -Mods.MenuID = "goonbase_mods_menu" -Mods.LoadedMods = Mods.LoadedMods or {} -Mods.EnabledMods = Mods.EnabledMods or {} - --- Localization -local Localization = GoonBase.Localization -Localization.ModsMenu_Button = "Modifications" -Localization.ModsMenu_ButtonDesc = "Control which modifications are loaded" -Localization.ModsMenu_ButtonInfoButton = "Help" -Localization.ModsMenu_ButtonInfoButtonDesc = "Show the modifications menu help" -Localization.ModsMenu_ButtonInfoButtonTitle = "Modifications" -Localization.ModsMenu_ButtonInfoButtonMessage = [[This menu allows you to enable and disable specific modifications in GoonMod. If a modification is enabled, it will load itself when Payday 2 is launched. - -Modifications highlighted in red require another modification to be enabled before they can be loaded, these modifications will be shown at the top of the screen. - -Once a modification is enabled/disabled, you will be required to restart your game to ensure that the modifications fully and successfully load or unload.]] -Localization.ModsMenu_ButtonInfoButtonAccept = "Close" - --- Menus -Hooks:Add("MenuManagerSetupCustomMenus", "MenuManagerSetupCustomMenus_ModsMenu", function( menu_manager, menu_nodes ) - - if menu_nodes.main ~= nil or menu_nodes.lobby ~= nil then - GoonBase.MenuHelper:NewMenu( Mods.MenuID ) - end - -end) - -Hooks:Add("MenuManagerSetupGoonBaseMenu", "MenuManagerSetupGoonBaseMenu_ModsMenu", function( menu_manager, menu_nodes ) - - if menu_nodes.main ~= nil or menu_nodes.lobby ~= nil then - - -- Options menu - GoonBase.MenuHelper:AddButton({ - id = "goonbase_mods_menu_button", - title = "ModsMenu_Button", - desc = "ModsMenu_ButtonDesc", - next_node = Mods.MenuID, - menu_id = "goonbase_options_menu", - priority = 901, - }) - - GoonBase.MenuHelper:AddDivider({ - id = "goonbase_mods_menu_divider", - menu_id = "goonbase_options_menu", - size = 16, - priority = 900, - }) - - -- Mods Menu - MenuCallbackHandler.open_mods_menu_help = function(this, item) - Mods:ShowHelpMenu() - end - - GoonBase.MenuHelper:AddButton({ - id = "goonbase_mods_menu_help_button", - title = "ModsMenu_ButtonInfoButton", - desc = "ModsMenu_ButtonInfoButtonDesc", - callback = "open_mods_menu_help", - menu_id = Mods.MenuID, - priority = 1000, - }) - - GoonBase.MenuHelper:AddDivider({ - id = "goonbase_mods_menu_help_divider", - menu_id = Mods.MenuID, - size = 16, - priority = 999, - }) - - -- Add mods - Mods:AddLoadedModsToMenu() - - end - -end) - -Hooks:Add("MenuManagerBuildCustomMenus", "MenuManagerBuildCustomMenus_ModsMenu", function( menu_manager, menu_nodes ) - - if menu_nodes.main ~= nil or menu_nodes.lobby ~= nil then - - menu_nodes[Mods.MenuID] = GoonBase.MenuHelper:BuildMenu( Mods.MenuID ) - Mods:VerifyAllRequirements() - - end - -end) - -function Mods:ShowHelpMenu() - - local title = managers.localization:text("ModsMenu_ButtonInfoButtonTitle") - local message = managers.localization:text("ModsMenu_ButtonInfoButtonMessage") - local menu_options = {} - menu_options[1] = { text = managers.localization:text("ModsMenu_ButtonInfoButtonAccept"), is_cancel_button = true } - local tradeMenu = SimpleMenu:New(title, message, menu_options) - tradeMenu:Show() - -end - -function Mods:RegisterMod( mod ) - Print("[Mods] Registering mod '" .. mod:ID() .. "'") - Mods.LoadedMods[ mod:ID() ] = mod -end - -function Mods:LoadMods() - - if GoonBase.SupportedVersion then - for k, v in pairs( GoonBase.ModFiles ) do - SafeDoFile( GoonBase.Path .. v ) - end - end - -end - -function Mods:SetupMods() - - for k, v in pairs( Mods.LoadedMods ) do - v:Setup() - end - -end - -function Mods:AddLoadedModsToMenu() - - for k, v in pairs( Mods.LoadedMods ) do - if v.HideInOptionsMenu ~= true then - v:SetupMenu() - end - end - -end - -function Mods:EnableMod( mod, enabled ) - if enabled == nil then - enabled = true - end - Mods.EnabledMods = Mods.EnabledMods or {} - Mods.EnabledMods[ mod:ID() ] = enabled -end - -function Mods:LoadEnabledMods() - Mods.EnabledMods = GoonBase.Options.EnabledMods or {} -end - -function Mods:SaveEnabledMods() - GoonBase.Options.EnabledMods = Mods.EnabledMods - GoonBase.Options:Save() -end - -function Mods:VerifyAllRequirements() - for k, v in pairs( Mods.LoadedMods ) do - v:VerifyRequirements() - end -end - -function Mods:IsEnabled( mod_id ) - if Mods.EnabledMods ~= nil and Mods.EnabledMods[mod_id] ~= nil then - return Mods.EnabledMods[mod_id]:IsEnabled() - end - return false -end - --- Hooks -Hooks:RegisterHook("GoonBaseRegisterMods") -Hooks:Add("GoonBasePostLoadMods", "GoonBasePostLoadMods_Mods", function() - - Print("[Mods] Loading Mods") - Mods:LoadEnabledMods() - Mods:LoadMods() - - Hooks:Call("GoonBaseRegisterMods") - - Print("[Mods] Setting up mods") - Mods:SetupMods() - -end) - --- Base Mod Definition -BaseMod = class() -BaseMod.id = "BaseMod" -BaseMod.Name = "Base Modification" -BaseMod.Desc = "The Base Modification" -BaseMod.MenuPrefix = "toggle_mod_" -BaseMod.MenuSuffix = "" -BaseMod.HideInOptionsMenu = false -BaseMod.Requirements = {} -BaseMod.Incompatibilities = {} -BaseMod.Path = nil -BaseMod.Priority = 0 -BaseMod.EnabledByDefault = false - -function BaseMod:ID() - return self.id -end - -function BaseMod:IsEnabled() - local requirements = self:RequirementsAreEnabled() - local incompatibles = self:IncompatibilitiesAreDisabled() - if not requirements or not incompatibles then - return false - end - if Mods.EnabledMods[ self:ID() ] == nil then - return self.EnabledByDefault - end - return Mods.EnabledMods[ self:ID() ] -end - -function BaseMod:GetName() - return self.Name -end - -function BaseMod:GetDesc() - return self.Desc -end - -function BaseMod:NameKey() - return self.MenuPrefix .. self:ID() .. self.MenuSuffix -end - -function BaseMod:DescKey() - return self.MenuPrefix .. self:ID() .. self.MenuSuffix .. "_desc" -end - -function BaseMod:SetPath(path) - self.Path = path -end - -function BaseMod:GetPath() - return self.Path -end - -function BaseMod:GetRequirements() - return self.Requirements -end - -function BaseMod:GetIncompatibilities() - return self.Incompatibilities -end - -function BaseMod:Setup() - self:SetupLocalization() -end - -function BaseMod:SetupLocalization() - local Localization = _G.GoonBase.Localization - Localization[ self:NameKey() ] = self:GetName() - self.DescOrig = self.Desc - Localization[ self:DescKey() ] = self:GetDesc() -end - -function BaseMod:SetupMenu() - - -- Callback - local menu_name = self.MenuPrefix .. self:ID() .. self.MenuSuffix - MenuCallbackHandler[menu_name] = function(this, item) - - local psuccess, perror = pcall(function() - - local enabled = item:value() == "on" and true or false - - Mods:EnableMod( self, enabled ) - if enabled then - self:OnEnabled() - else - self:OnDisabled() - end - - Mods:SaveEnabledMods() - Mods:VerifyAllRequirements() - - end) - if not psuccess then - Print("[Error] " .. perror) - end - - end - - -- Add to menu - GoonBase.MenuHelper:AddToggle({ - id = menu_name, - title = self:NameKey(), - desc = self:DescKey(), - callback = menu_name, - value = self:IsEnabled(), - disabled_color = Color( 0.8, 0.3, 0.3, 0.3 ), - menu_id = Mods.MenuID, - priority = self.Priority or 0 - }) - -end - -function BaseMod:OnEnabled() - Print("[Mods] Mod '" .. self:ID() .. "' enabled") -end - -function BaseMod:OnDisabled() - Print("[Mods] Mod '" .. self:ID() .. "' disabled") -end - -function BaseMod:VerifyRequirements() - self:ResetLocalization() - local enabled = (self:IncompatibilitiesAreDisabled() and self:RequirementsAreEnabled()) and true or false - self:SetEnabledModMenuItem( enabled ) -end - -function BaseMod:RequirementsAreEnabled() - - local enabled = true - for k, v in pairs( self:GetRequirements() ) do - if v ~= nil and Mods.LoadedMods[v] ~= nil and not Mods.LoadedMods[v]:IsEnabled() then - enabled = false - end - end - - self:ModifyLocalizationDescWithRequirements(enabled) - return enabled - -end - -function BaseMod:IncompatibilitiesAreDisabled() - - local enabled = true - for k, v in pairs( self:GetIncompatibilities() ) do - if Mods.LoadedMods[v]:IsEnabled() then - enabled = false - end - end - - self:ModifyLocalizationDescWithIncompatibilities(enabled) - return enabled - -end - -function BaseMod:ResetLocalization() - local Localization = _G.GoonBase.Localization - Localization[ self:DescKey() ] = self.DescOrig -end - -function BaseMod:ModifyLocalizationDescWithRequirements(enabled) - - if enabled then - return - end - - local Localization = _G.GoonBase.Localization - local str = self.DescOrig - - local reqsStr = "" - for k, v in pairs( self:GetRequirements() ) do - if not Mods.LoadedMods[v]:IsEnabled() then - if reqsStr ~= "" then - reqsStr = reqsStr .. ", " - end - reqsStr = reqsStr .. Mods.LoadedMods[v]:GetName() - end - end - - str = str .. "\n" - str = str .. "Requires: " .. reqsStr - - Localization[ self:DescKey() ] = str - -end - -function BaseMod:ModifyLocalizationDescWithIncompatibilities(enabled) - - if enabled then - return - end - - local Localization = _G.GoonBase.Localization - local str = self.DescOrig - - local reqsStr = "" - for k, v in pairs( self:GetIncompatibilities() ) do - if Mods.LoadedMods[v]:IsEnabled() then - if reqsStr ~= "" then - reqsStr = reqsStr .. ", " - end - reqsStr = reqsStr .. Mods.LoadedMods[v]:GetName() - end - end - - str = str .. "\n" - str = str .. "Incompatible with: " .. reqsStr - - Localization[ self:DescKey() ] = str - -end - -function BaseMod:SetEnabledModMenuItem(enabled) - - local menu = GoonBase.MenuHelper:GetMenu( Mods.MenuID ) - for k, v in pairs( menu["_items"] ) do - local menu_name = v["_parameters"]["name"]:gsub(self.MenuPrefix, "") - if menu_name == self:ID() then - v:set_enabled( enabled ) - if not enabled and v:value() == "on" then - v:set_value( "off" ) - end - v:dirty() - end - end - -end --- END OF FILE diff --git a/GoonBase/req/network.lua b/GoonBase/req/network.lua deleted file mode 100644 index 5b6e404..0000000 --- a/GoonBase/req/network.lua +++ /dev/null @@ -1,245 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -_G.GoonBase.Network = _G.GoonBase.Network or {} -local GNetwork = _G.GoonBase.Network - -GNetwork.HiddenChannel = 4 -GNetwork.AllPeers = "GNAP" -GNetwork.AllPeersString = "{1}/{2}/{3}" -GNetwork.SinglePeer = "GNSP" -GNetwork.SinglePeerString = "{1}/{2}/{3}/{4}" -GNetwork.ExceptPeer = "GNEP" -GNetwork.ExceptPeerString = "{1}/{2}/{3}/{4}" -GNetwork.Split = "[/]" - -function GNetwork:IsMultiplayer() - if managers.network == nil then - return false - end - return managers.network:session() -end - -function GNetwork:IsHost() - if not Network then - return false - end - return not Network:is_client() -end - -function GNetwork:IsClient() - if not Network then - return false - end - return Network:is_client() -end - -function GNetwork:LocalPeerID() - if managers.network == nil or managers.network:session() == nil or managers.network:session():local_peer() == nil then - return 0 - end - return managers.network:session():local_peer():id() or 0 -end - -function GNetwork:TableToString(tbl) - local str = "" - for k, v in pairs(tbl) do - if str ~= "" then - str = str .. "," - end - str = str .. ("{0}|{1}"):gsub("{0}", tostring(k)):gsub("{1}", tostring(v)) - end - return str -end - -function GNetwork:StringToTable(str) - local tbl = {} - local tblPairs = string.split( str, "[,]" ) - for k, v in pairs(tblPairs) do - local pairData = string.split( v, "[|]" ) - tbl[ pairData[1] ] = pairData[2] - end - return tbl -end - -function GNetwork:GetNameFromPeerID(id) - - for k, v in pairs( managers.network:session():peers() ) do - if k == id then - return v:name() - end - end - - return "No Name" - -end - -function GNetwork:GetPeers() - return managers.network:session():peers() -end - -function GNetwork:GetNumberOfPeers() - local i = 0 - for k, v in pairs( managers.network:session():peers() ) do - i = i + 1 - end - return i -end - -function GNetwork:SendToPeers(type, data) - local dataString = GNetwork.AllPeersString - dataString = dataString:gsub("{1}", GNetwork.AllPeers) - dataString = dataString:gsub("{2}", type) - dataString = dataString:gsub("{3}", data) - GNetwork:SendStringThroughChat(dataString) -end - -function GNetwork:SendToPeer(peer, type, data) - local dataString = GNetwork.SinglePeerString - dataString = dataString:gsub("{1}", GNetwork.SinglePeer) - dataString = dataString:gsub("{2}", peer) - dataString = dataString:gsub("{3}", type) - dataString = dataString:gsub("{4}", data) - GNetwork:SendStringThroughChat(dataString) -end - -function GNetwork:SendToPeersExcept(peer, type, data) - local dataString = GNetwork.ExceptPeerString - local peerStr = peer - if type(peer) == "table" then - peerStr = "" - for k, v in pairs(peer) do - if peerStr ~= "" then - peerStr = peerStr .. "," - end - peerStr = peerStr .. tostring(v) - end - end - - dataString = dataString:gsub("{1}", GNetwork.ExceptPeer) - dataString = dataString:gsub("{2}", peerStr) - dataString = dataString:gsub("{3}", type) - dataString = dataString:gsub("{4}", data) - GNetwork:SendStringThroughChat(dataString) -end - -function GNetwork:SendStringThroughChat(message) - if ChatManager._receivers == nil then - ChatManager._receivers = {} - end - ChatManager:send_message( GNetwork.HiddenChannel, tostring(GNetwork:LocalPeerID()), message ) -end - -Hooks:Add("ChatManagerOnReceiveMessage", "ChatManagerOnReceiveMessage_Network", function(channel_id, name, message, color, icon) - - local s = "[{1}] {2}: {3}" - s = s:gsub("{1}", channel_id) - s = s:gsub("{2}", name) - s = s:gsub("{3}", message) - Print(s) - - local senderID = nil - if GNetwork:IsMultiplayer() then - - if name == managers.network:session():local_peer():name() then - senderID = GNetwork:LocalPeerID() - end - - for k, v in pairs( managers.network:session():peers() ) do - if v:name() == name then - senderID = k - end - end - - end - - if senderID == GNetwork:LocalPeerID() then return end - - if tonumber(channel_id) == GNetwork.HiddenChannel then - GNetwork:ProcessChatString(senderID or name, message, color, icon) - end - -end) - -Hooks:RegisterHook("NetworkReceivedData") -function GNetwork:ProcessChatString(sender, message, color, icon) - - local splitData = string.split( message, GNetwork.Split ) - local msgType = splitData[1] - if msgType == GNetwork.AllPeers then - GNetwork:ProcessAllPeers(sender, message, color, icon) - end - if msgType == GNetwork.SinglePeer then - GNetwork:ProcessSinglePeer(sender, message, color, icon) - end - if msgType == GNetwork.ExceptPeer then - GNetwork:ProcessExceptPeer(sender, message, color, icon) - end - -end - -function GNetwork:ProcessAllPeers(sender, message, color, icon) - local splitData = string.split( message, GNetwork.Split ) - Hooks:Call("NetworkReceivedData", sender, splitData[2], splitData[3]) -end - -function GNetwork:ProcessSinglePeer(sender, message, color, icon) - - local splitData = string.split( message, GNetwork.Split ) - local toPeer = tonumber( splitData[2] ) - - if toPeer == GNetwork:LocalPeerID() then - Hooks:Call("NetworkReceivedData", sender, splitData[3], splitData[4]) - end - -end - -function GNetwork:ProcessExceptPeer(sender, message, color, icon) - - local splitData = string.split( message, GNetwork.Split ) - local exceptedPeers = string.split( splitData[2], "[,]" ) - - local excepted = false - for k, v in pairs(exceptedPeers) do - if tonumber(v) == GNetwork:LocalPeerID() then - excepted = true - end - end - - if not excepted then - Hooks:Call("NetworkReceivedData", sender, splitData[3], splitData[4]) - end - -end - --- Extensions - -GNetwork._networked_colour_string = "r:{1}|g:{2}|b:{3}|a:{4}" -function GNetwork:PrepareNetworkedColourString(col) - local dataString = GNetwork._networked_colour_string - dataString = dataString:gsub("{1}", math.round_with_precision(col.r, 4)) - dataString = dataString:gsub("{2}", math.round_with_precision(col.g, 4)) - dataString = dataString:gsub("{3}", math.round_with_precision(col.b, 4)) - dataString = dataString:gsub("{4}", math.round_with_precision(col.a, 4)) - return dataString -end - -function GNetwork:NetworkedColourStringToColour(str) - - local data = string.split( str, "[|]" ) - if #data < 4 then - return nil - end - - local split_str = "[:]" - local r = tonumber(string.split(data[1], split_str)[2]) - local g = tonumber(string.split(data[2], split_str)[2]) - local b = tonumber(string.split(data[3], split_str)[2]) - local a = tonumber(string.split(data[4], split_str)[2]) - - return Color(a, r, g, b) - -end --- END OF FILE diff --git a/GoonBase/req/options.lua b/GoonBase/req/options.lua deleted file mode 100644 index 527b77d..0000000 --- a/GoonBase/req/options.lua +++ /dev/null @@ -1,122 +0,0 @@ ----------- --- Payday 2 GoonMod, Weapon Customizer Beta, built on 12/30/2014 6:10:13 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -_G.GoonBase.Options = _G.GoonBase.Options or {} -local Options = _G.GoonBase.Options -Options.SaveFile = GoonBase.Path .. "options.ini" - -local OptionsMenuID = "goonbase_options_menu" -Hooks:RegisterHook( "MenuManagerSetupGoonBaseMenu" ) -Hooks:RegisterHook( "MenuManagerPostSetupGoonBaseMenu" ) - -Hooks:Add("MenuManagerSetupCustomMenus", "MenuManagerSetupCustomMenus_OptionsMenu", function(menu_manager, mainmenu_nodes) - GoonBase.MenuHelper:NewMenu( OptionsMenuID ) -end) - -Hooks:Add("MenuManagerPopulateCustomMenus", "MenuManagerPopulateCustomMenus_OptionsMenu", function(menu_manager, mainmenu_nodes) - Hooks:Call( "MenuManagerSetupGoonBaseMenu", menu_manager, mainmenu_nodes ) -end) - -Hooks:Add("MenuManagerBuildCustomMenus", "MenuManagerBuildCustomMenus_OptionsMenu", function(menu_manager, mainmenu_nodes) - mainmenu_nodes[OptionsMenuID] = GoonBase.MenuHelper:BuildMenu( OptionsMenuID ) - GoonBase.MenuHelper:AddMenuItem( mainmenu_nodes.options, OptionsMenuID, "GoonBaseOptionsName", "GoonBaseOptionsDesc", 5 ) - Hooks:Call( "MenuManagerPostSetupGoonBaseMenu", menu_manager, mainmenu_nodes ) -end) - -function Options:GetSaveString() - - local contents = ""; - for k, v in pairs( Options ) do - - if type(v) == "table" then - contents = string.format( "%s[%s]\n", contents, tostring(k) ) - for a, b in pairs( v ) do - contents = string.format( "%s%s=%s\n", contents, tostring(a), tostring(b) ) - end - end - - end - - return contents - -end - -function Options:UsingDefaults() - if self._default then - return true - end - return false -end - -Hooks:RegisterHook("OptionsRegisterDefaultOptions") -function Options:LoadDefaults() - self._default = nil - Hooks:Call("OptionsRegisterDefaultOptions") - self:Save() -end - -function Options:Save(fileName) - - if fileName == nil then - fileName = Options.SaveFile - end - - local file = io.open(fileName, "w+") - file:write( Options:GetSaveString() ) - file:close() - -end - -function Options:Load(fileName) - - if fileName == nil then - fileName = Options.SaveFile - end - - local file = io.open(fileName, 'r') - local key - - if file == nil then - Print( "Could not open file (" .. fileName .. "), using default options..." ) - self._default = true - return - end - - for line in file:lines() do - - local loadKey = line:match('^%[([^%[%]]+)%]$') - - if loadKey then - key = tonumber(loadKey) and tonumber(loadKey) or loadKey - Options[key] = Options[key] or {} - end - - local param, val = line:match('^([%w|_]+)%s-=%s-(.+)$') - - if param and val ~= nil then - - if tonumber(val) then - val = tonumber(val) - elseif val == "true" then - val = true - elseif val == "false" then - val = false - end - - if tonumber(param) then - param = tonumber(param) - end - - Options[key][param] = val - - end - - end - - file:close() - -end -Options:Load() --- END OF FILE diff --git a/GoonBase/req/updates.lua b/GoonBase/req/updates.lua deleted file mode 100644 index febbf88..0000000 --- a/GoonBase/req/updates.lua +++ /dev/null @@ -1,764 +0,0 @@ ----------- --- Payday 2 GoonMod, Public Release Beta 2, built on 1/9/2015 9:30:33 PM --- Copyright 2014, James Wilkinson, Overkill Software ----------- - -_G.GoonBase.Updates = _G.GoonBase.Updates or {} -local Updates = _G.GoonBase.Updates - -local UPrint = function(str) - Print("[Update] " .. str) -end - --- Update -Updates.HasCheckedForUpdates = false -Updates.BasePath = "https://raw.githubusercontent.com/JamesWilko/GoonMod/master/" -Updates.BasePathToken = "" -Updates.Version = "update_version.txt" -Updates.FileList = "update_list.txt" -Updates.FileExts = { - ["lua"] = true, - ["txt"] = true, - ["ini"] = true, - ["yml"] = true, - ["dll"] = true -} -Updates.PatchNotesURL = "https://google.com/" -Updates.UpdateFileLocation = "GoonBase/req/updates.lua" -Updates.HookFileLocation = "PD2Hook.yml" -Updates.FilesToUpdate = {} -Updates.FilesCurrentlyUpdating = 0 -Updates.FileCurrentlyProcessing = 0 -Updates.CurrentFileRetries = 0 -Updates.MaxFileRetries = 3 -Updates.WaitingWindow = nil - --- Errors -Updates.ErrorList = {} - --- Options -GoonBase.Options.Updates = GoonBase.Options.Updates or {} -GoonBase.Options.Updates.FirstTimeStartup = true -GoonBase.Options.Updates.CheckForUpdates = true -GoonBase.Options.Updates.BypassUnsupported = false -GoonBase.Options.Updates.GameVersion = "" -GoonBase.Options.Updates.ShownUpdateWindow = false -GoonBase.Options:Load() - --- Localization -local Localization = GoonBase.Localization -Localization.Updates_FirstTitle = "GoonMod Automatic Updates" -Localization.Updates_FirstMessage = [[GoonMod has an automatic updates feature. This will allow you to update some aspects of GoonMod without installing the updates yourself. -You will be notified and asked before an update occurs, and you can change this setting at anytime via the options menu. - -Would you like to enable automatic updates? (Highly Recommended) -]] -Localization.Updates_FirstAccept = "Enable Automatic Updates" -Localization.Updates_FirstDecline = "Disable Automatic Updates" - -Localization.Updates_AvailableTitle = "GoonMod Update Available" -Localization.Updates_AvailableMessage = "A new update for GoonMod is available. Would you like to update now?" -Localization.Updates_AllowUpdate = "Update Now" -Localization.Updates_DontUpdate = "Later" -Localization.Updates_ViewPatchNotes = "View Update Notes" - -Localization.Updates_UpdatingTitle = "Updating GoonMod" -Localization.Updates_UpdatingMessage = "Updating GoonMod. Please do not close your game during this time, doing so may corrupt your mod installation." - -Localization.Updates_ManualTitle = "Manual Update Required" -Localization.Updates_ManualMessage = "A manual update to GoonMod is required. You can find the download link, payday 2 folder shortcut, and update notes available below." -Localization.Updates_ManualAccept = "View Update" -Localization.Updates_ManualPaydayFolder = "Open Payday 2 Folder" -Localization.Updates_ManualLater = "Update Later" - -Localization.Updates_NoUpdateTitle = "No Update Required" -Localization.Updates_NoUpdateMessage = [[Your GoonMod installation is currently up-to-date. - -If necessary, you can force a re-download of the latest files by clicking on the Force Redownload button.]] -Localization.Updates_NoUpdateAccept = "OK" -Localization.Updates_NoUpdateForce = "Force Redownload" - -Localization.Updates_VersionMismatchTitle = "Unsupported Version" -Localization.Updates_VersionMismatchMessage = [[Your version of Payday 2 is currently unsupported, your game has probably updated. - -Your Modifications menu may appear empty, and any enabled mods have been disabled to prevent any unwanted crashes. - -An update may be available for download shortly, but if you wish to force GoonMod to work, and to load your mods, you can do so from the options menu.]] -Localization.Updates_VersionMismatchAccept = "OK" - -Localization.Updates_UpdateCompleteTitle = "Update Complete!" -Localization.Updates_UpdateCompleteMessage = "Update completed successfully! Please restart Payday 2 to complete the update." -Localization.Updates_UpdateCompleteAccept = "Restart Now" -Localization.Updates_UpdateCompleteLater = "Restart Later" - -Localization.Updates_UpdateErrorTitle = "Warning!" -Localization.Updates_UpdateErrorMessage = [[An error occurred during the update. You may be required to reinstall GoonMod if problems occur. -Please contact, and send your GoonBase.log file from your Payday 2 folder to the mod author if you experience further problems.]] -Localization.Updates_UpdateErrorAccept = "OK" - -Localization.Updates_Options_CheckForUpdates = "Automically Check For Updates" -Localization.Updates_Options_CheckForUpdatesDesc = "Automatically check for and download GoonMod updates" -Localization.Updates_Options_CheckNow = "Check For Updates Now" -Localization.Updates_Options_CheckNowDesc = "Immediately check for any updates to GoonMod" -Localization.Updates_Options_IgnoreUnsupported = "Ignore Unsupported Version" -Localization.Updates_Options_IgnoreUnsupportedDesc = "Ignore the unsupported version check and run all GoonMod modules anyway (Requires Restart)" - --- Hooks -Hooks:Add("MenuManagerOnOpenMenu", "MenuManagerOnOpenMenu_Updates", function( menu_manager, menu, position ) - - -- Check for updates after going to the main menu - if menu == "menu_main" then - - if GoonBase.Options.Updates.ShownUpdateWindow then - return - end - - -- Check for first time setup - if GoonBase.Options.Updates.FirstTimeStartup then - if not Updates:IsSupportedVersion() then - Updates:GameVersionMismatchWindow() - end - Queue:Add("MenuManagerInitialize_Updates_FirstTime", Updates.FirstTimeSetup, 0.25) - return - end - - -- Check for updates - Updates:InitialCheckForUpdates() - - end - -end) - -Hooks:Add("MenuManagerSetupGoonBaseMenu", "MenuManagerSetupGoonBaseMenu_Updates", function( menu_manager ) - - local success, err = pcall(function() - - MenuCallbackHandler.toggle_updates_checkforupdates = function(this, item) - GoonBase.Options.Updates.CheckForUpdates = item:value() == "on" and true or false - GoonBase.Options:Save() - end - - MenuCallbackHandler.button_check_for_updates = function(this, item) - Updates._force_check = true - Updates:CheckForUpdates(true) - end - - MenuCallbackHandler.toggle_unsupported_bypass = function(this, item) - GoonBase.Options.Updates.BypassUnsupported = item:value() == "on" and true or false - GoonBase.Options:Save() - end - - GoonBase.MenuHelper:AddToggle({ - id = "toggle_updates", - title = "Updates_Options_CheckForUpdates", - desc = "Updates_Options_CheckForUpdatesDesc", - callback = "toggle_updates_checkforupdates", - value = GoonBase.Options.Updates.CheckForUpdates, - menu_id = "goonbase_options_menu", - priority = 999 - }) - - GoonBase.MenuHelper:AddButton({ - id = "button_updates_check_now", - title = "Updates_Options_CheckNow", - desc = "Updates_Options_CheckNowDesc", - callback = "button_check_for_updates", - menu_id = "goonbase_options_menu", - priority = 998, - }) - - - if GoonBase.Options.Updates.BypassUnsupported or not Updates:IsSupportedVersion() then - GoonBase.MenuHelper:AddToggle({ - id = "toggle_unsupported_bypass", - title = "Updates_Options_IgnoreUnsupported", - desc = "Updates_Options_IgnoreUnsupportedDesc", - callback = "toggle_unsupported_bypass", - value = GoonBase.Options.Updates.BypassUnsupported, - menu_id = "goonbase_options_menu", - priority = 1000 - }) - end - - end) - if not success then PrintTable(err) end - -end) - -Hooks:Add("SetupOnQuit", "SetupOnQuit_Updates", function(setup) - GoonBase.Options.Updates.ShownUpdateWindow = false - GoonBase.Options:Save() -end) - -function Updates:InitialCheckForUpdates() - - -- Check game version - if not Updates:IsSupportedVersion() then - Updates:GameVersionMismatchWindow() - end - - -- Only check for updates immediately if allowed - if GoonBase.Options.Updates.CheckForUpdates then - Queue:Add("MenuManagerInitialize_Updates", Updates.CheckForUpdates, 1) - end - -end - --- First Time Setup -function Updates:FirstTimeSetup() - - local title = managers.localization:text("Updates_FirstTitle") - local message = managers.localization:text("Updates_FirstMessage") - local menuOptions = {} - menuOptions[1] = { - text = managers.localization:text("Updates_FirstAccept"), - callback = Updates.FirstTimeSetup_UpdatesOn, - } - menuOptions[2] = { - text = managers.localization:text("Updates_FirstDecline"), - callback = Updates.FirstTimeSetup_UpdatesOff, - } - local updateWindow = SimpleMenu:New(title, message, menuOptions) - updateWindow:Show() - -end - -function Updates.FirstTimeSetup_UpdatesOn() - -- Setup options - GoonBase.Options.Updates.CheckForUpdates = true - GoonBase.Options.Updates.FirstTimeStartup = false - GoonBase.Options:Save() - - -- Check for updates - Updates:InitialCheckForUpdates() -end - -function Updates.FirstTimeSetup_UpdatesOff() - -- Setup options - GoonBase.Options.Updates.CheckForUpdates = false - GoonBase.Options.Updates.FirstTimeStartup = false - GoonBase.Options:Save() -end - --- Update Check -function Updates:CheckForUpdates( force ) - - if not force and not GoonBase.Options.Updates.CheckForUpdates then return end - - if force or not Updates.HasCheckedForUpdates then - UPrint("Requesting update version...") - Print( Updates.BasePath .. Updates.Version .. Updates.BasePathToken ) - Steam:http_request(Updates.BasePath .. Updates.Version .. Updates.BasePathToken, callback(Updates, Updates, "UpdateVersionCallback")) - end - -end - -function Updates:GetUpdateFileList() - - UPrint("Requesting file list from server...") - Steam:http_request(Updates.BasePath .. Updates.FileList .. Updates.BasePathToken, callback(Updates, Updates, "UpdateListCallback")) - -end - --- Update menu -function Updates:RequestUpdatePermission() - - if not GoonBase.Options.Updates.CheckForUpdates then return end - - local title = managers.localization:text("Updates_AvailableTitle") - local message = managers.localization:text("Updates_AvailableMessage") - local menuOptions = {} - menuOptions[1] = { - text = managers.localization:text("Updates_AllowUpdate"), - callback = Updates.BeginUpdateFiles, - is_cancel_button = true - } - menuOptions[2] = { - text = managers.localization:text("Updates_ViewPatchNotes"), - callback = Updates.OpenPatchNotes, - } - menuOptions[3] = { - text = managers.localization:text("Updates_DontUpdate"), - is_cancel_button = true - } - - local updateWindow = SimpleMenu:New(title, message, menuOptions) - updateWindow:Show() - -end - -function Updates:GameVersionMismatchWindow() - - if GoonBase.Options.Updates.GameVersion == Application:version() then - return - end - - -- Show message - local title = managers.localization:text("Updates_VersionMismatchTitle") - local message = managers.localization:text("Updates_VersionMismatchMessage") - local menuOptions = {} - menuOptions[1] = { - text = managers.localization:text("Updates_VersionMismatchAccept"), - is_cancel_button = true - } - local updateWindow = SimpleMenu:New(title, message, menuOptions) - updateWindow:Show() - - -- Don't show message again - GoonBase.Options.Updates.GameVersion = Application:version() - GoonBase.Options:Save() - -end - -function Updates:ManualUpdateWindow() - - local title = managers.localization:text("Updates_ManualTitle") - local message = managers.localization:text("Updates_ManualMessage") - local menuOptions = {} - menuOptions[1] = { - text = managers.localization:text("Updates_ManualAccept"), - callback = Updates.ManualUpdateCallback, - is_cancel_button = true - } - menuOptions[1] = { - text = managers.localization:text("Updates_ManualPaydayFolder"), - callback = Updates.ManualOpenPaydayFolder, - is_cancel_button = true - } - menuOptions[3] = { - text = managers.localization:text("Updates_ManualLater"), - is_cancel_button = true - } - local updateWindow = SimpleMenu:New(title, message, menuOptions) - updateWindow:Show() - -end - --- File update -function Updates.BeginUpdateFiles() - - UPrint("Beginning Updates...") - Updates:ShowUpdatingWindow() - - for k, v in pairs( Updates.FilesToUpdate ) do - Updates.FilesCurrentlyUpdating = Updates.FilesCurrentlyUpdating + 1 - end - - Updates.FileCurrentlyProcessing = 0 - Updates:UpdateNextFileInList() - -end - -function Updates:UpdateNextFileInList() - - Updates.FileCurrentlyProcessing = Updates.FileCurrentlyProcessing + 1 - local file = Updates.FilesToUpdate[ Updates.FileCurrentlyProcessing ] - - if file ~= nil then - file = file:gsub("\\", "/") - UPrint("Updating " .. file .. " (" .. Updates.BasePath .. file .. ")") - Steam:http_request(Updates.BasePath .. file .. Updates.BasePathToken, callback(Updates, Updates, "UpdateFileCallback", file)) - end - -end - -function Updates:RetryCurrentFile() - - Updates.CurrentFileRetries = Updates.CurrentFileRetries + 1 - - local psuccess, perror = pcall(function() - - if Updates.CurrentFileRetries > Updates.MaxFileRetries then - local errorString = "File '" .. fileName .. "' exceeded the maximum download retries, skipping..." - UPrint(errorString) - table.insert( Updates.ErrorList, errorString ) - Updates:UpdateNextFileInList() - return - end - - local file = Updates.FilesToUpdate[ Updates.FileCurrentlyProcessing ] - if file ~= nil then - UPrint("Retrying " .. file) - Steam:http_request(Updates.BasePath .. file .. Updates.BasePathToken, callback(Updates, Updates, "UpdateFileCallback", file)) - end - - end) - if not psuccess then - Print("[Error] " .. perror) - end - -end - -function Updates.OpenPatchNotes() - UPrint("Displaying patch notes...") - Updates:ShowPatchNotes() - Updates:RequestUpdatePermission() -end - -function Updates:ShowPatchNotes() - Steam:overlay_activate("url", Updates.PatchNotesURL) -end - -function Updates.ManualUpdateCallback() - Updates:ShowPatchNotes() -end - -function Updates.ManualOpenPaydayFolder() - if SystemInfo:platform() == Idstring("WIN32") then - os.execute( "explorer " .. Application:base_path() ) - end -end - -function Updates:ShowNoUpdates() - - local title = managers.localization:text("Updates_NoUpdateTitle") - local message = managers.localization:text("Updates_NoUpdateMessage") - local menuOptions = {} - menuOptions[1] = { - text = managers.localization:text("Updates_NoUpdateForce"), - callback = Updates.ForceRedownloadMod, - is_cancel_button = true - } - menuOptions[2] = { - text = managers.localization:text("Updates_NoUpdateAccept"), - is_cancel_button = true - } - local updateWindow = SimpleMenu:New(title, message, menuOptions) - updateWindow:Show() - -end - -function Updates.ForceRedownloadMod() - Updates._force_redownload = true - Updates:CheckForUpdates( true ) -end - --- Updating Window -function Updates:ShowUpdatingWindow() - - -- Display Update Window - local title = managers.localization:text("Updates_UpdatingTitle") - local message = managers.localization:text("Updates_UpdatingMessage") - Updates.WaitingWindow = SimpleMenu:New(title, message, {}) - Updates.WaitingWindow.dialog_data.no_buttons = true - Updates.WaitingWindow.dialog_data.indicator = true - Updates.WaitingWindow:Show() - - -- Check if updates have completed - Queue:Add("Updates_CheckUpdateStatus", Updates.CheckUpdatingStatus, 3) - -end - -function Updates:CheckUpdatingStatus() - - if Updates.FilesCurrentlyUpdating > 0 then - -- Check again if updates have completed - Queue:Add("Updates_CheckUpdateStatus", Updates.CheckUpdatingStatus, 1) - else - -- Updates finished - Updates:CloseUpdatingWindow() - end - -end - -function Updates:CloseUpdatingWindow() - - -- Close window if updates have completed - managers.system_menu:close(Updates.WaitingWindow.dialog_data.id) - Updates.WaitingWindow.visible = false - - -- Show completion window - local title = managers.localization:text("Updates_UpdateCompleteTitle") - local message = managers.localization:text("Updates_UpdateCompleteMessage") - local menuOptions = {} - menuOptions[1] = { - text = managers.localization:text("Updates_UpdateCompleteAccept"), - callback = Updates.ForceCloseGame, - is_cancel_button = true - } - menuOptions[2] = { - text = managers.localization:text("Updates_UpdateCompleteLater"), - is_cancel_button = true - } - local updateWindow = SimpleMenu:New(title, message, menuOptions) - updateWindow:Show() - - -- Show unsupported message again in the future - GoonBase.Options.Updates.ShownUnsupportedMessage = false - GoonBase.Options:Save() - -end - -function Updates.ForceCloseGame() - managers.savefile:save_progress("local_hdd") - setup:quit() -end - --- Callback -function Updates:UpdateVersionCallback( success, file ) - - local psuccess, perror = pcall(function() - - UPrint("Received callback: " .. tostring(success)) - - -- Don't process failed request - if not success then - UPrint("Couldn't retreive latest version") - Print(file) - return - end - - -- Parse info - local shouldUpdate = false - local lines = string.split( file, "\n" ) - local serverVersion = tonumber(lines[1]) - local serverGameVersion = lines[2] - local patchNotes = lines[3] - local manualUpdate = lines[4] - - if serverVersion == nil then - UPrint("Could not process server version") - return - end - - -- Check version number - UPrint("Versions - Local: " .. GoonBase.Version .. " / Remote: " .. serverVersion) - if type(serverVersion) == "number" and type(GoonBase.Version) == "number" then - if serverVersion > GoonBase.Version then - UPrint("New version available, version " .. serverVersion) - shouldUpdate = true - end - end - - -- Forced redownload - if Updates._force_redownload then - shouldUpdate = true - end - - -- Forced check if update required - if not shouldUpdate then - - -- Show forced check for updates screen - if Updates._force_check then - Updates:ShowNoUpdates() - Updates._force_check = nil - end - - return - end - - -- Get patch notes URL - Updates.PatchNotesURL = patchNotes - - -- Check if manual update is required - local manualUpdateString = "manual=true" - local req = string.match( manualUpdate, "(" .. manualUpdateString .. ")" ) - if req ~= nil and req == manualUpdateString then - -- Manual Update - Updates:ManualUpdateWindow() - else - -- Automatic Update - Updates:GetUpdateFileList() - end - - GoonBase.Options.Updates.ShownUpdateWindow = true - GoonBase.Options:Save() - - end) - if not psuccess then - Print("[Error] " .. perror) - end -end - -function Updates:UpdateListCallback(success, file) - - UPrint("Received callback: " .. tostring(success)) - - -- Don't process failed request - if not success then - UPrint("Couldn't retreive update list") - return - end - - UPrint("Processing update list...") - - -- Clear files list - Updates.FilesToUpdate = {} - - -- Check files to update - local files = string.split(file, "[\n]") - for k, v in pairs(files) do - local f = string.split(v, "[.]") - if Updates.FileExts[ f[2] ] == true then - table.insert( Updates.FilesToUpdate, v ) - end - end - - Updates.HasCheckedForUpdates = true - - if not Updates._force_redownload then - self:RequestUpdatePermission() - else - Updates.BeginUpdateFiles() - end - -end - -function Updates:UpdateFileCallback(fileName, success, data) - - -- Decrement files updating - Updates.FilesCurrentlyUpdating = Updates.FilesCurrentlyUpdating - 1 - - -- Check if successful - if not success then - UPrint("Could not update file '" .. fileName .. "'") - Updates:RetryCurrentFile() - return - end - - -- Write file to disk - local redownloadFile = false - local writeSuccess, writeError = pcall(function() - - -- Remove garbage data from end of files - local file_ending = "-- END OF FILE" - if fileName == self.UpdateFileLocation then - file_ending = "--" .. "#" .. " END OF FILE" - end - data = string.gsub( data, "(" .. file_ending .. ".*)", "" ) - - -- Process hook file separately - if fileName == self.HookFileLocation then - data = self:ProcessHookFile( data ) - end - - -- Save file - local file = io.open(fileName, "w+") - io.output(file) - io.write(data) - io.close(file) - - end) - - -- Check for write success - if not redownloadFile then - if writeSuccess then - UPrint("Successfully updated file '" .. fileName .. "'") - else - local errorString = "Error while updating file '" .. fileName .. "'\n" .. writeError - UPrint(errorString) - table.insert( Updates.ErrorList, errorString ) - end - else - Updates:RetryCurrentFile() - return - end - - -- Continue - Updates:UpdateNextFileInList() - -end - -function Updates:ProcessHookFile( data ) - - local localfile_data = {} - local update_data = {} - local write_data = true - - -- Get local data - write_data = true - for line in io.lines( self.HookFileLocation ) do - - if line ~= nil then - - local linetrim = line:gsub("^%s*(.-)%s*$", "%1") - if linetrim == "# GOONBASE" then - write_data = false - table.insert( localfile_data, line ) - elseif linetrim == "# END" then - write_data = true - table.insert( localfile_data, line ) - elseif write_data then - table.insert( localfile_data, line ) - end - - end - - end - - -- Get data from update - local data_lines = string.split( data, "[\n]" ) - write_data = false - for l, line in pairs( data_lines ) do - - if line ~= nil then - - local linetrim = line:gsub("^%s*(.-)%s*$", "%1") - if linetrim == "# GOONBASE" then - write_data = true - elseif linetrim == "# END" then - write_data = false - elseif write_data then - table.insert( update_data, line ) - end - - end - - end - - -- Merge data - local merged = false - for l, line in pairs( localfile_data ) do - - local linetrim = line:gsub("^%s*(.-)%s*$", "%1") - if linetrim == "# GOONBASE" and not merged then - for i = 1, #update_data, 1 do - table.insert( localfile_data, l + i, update_data[i] ) - end - merged = true - end - - end - - -- Build string - local new_data = "" - for l, line in pairs( localfile_data ) do - if line ~= nil then - new_data = new_data .. line .. "\n" - end - end - - return new_data - -end - --- Parse string into version -function Updates:ParseUpdateString(str) - return str:split("[.]") -end - -function Updates:CompareVersionStrings(local_version_str, other_version_str) - - local local_version = self:ParseUpdateString(local_version_str) - local other_version = self:ParseUpdateString(other_version_str) - - -- Return false if version is out of date - for i = 1, 3, 1 do - if local_version[i] < other_version[i] then - return false - end - end - - return true - -end - -function Updates:IsSupportedVersion() - if GoonBase.Options.Updates.BypassUnsupported then - return true - end - return Updates:CompareVersionStrings(GoonBase.GameVersion, Application:version()) -end - ---# END OF FILE --- END OF FILE diff --git a/IPHLPAPI.dll b/IPHLPAPI.dll deleted file mode 100644 index 42cac14998053b745d0a08fbe210d1a5268e5d31..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 232448 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 z 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 E01g}>`Tzg` diff --git a/PD2Hook.yml b/PD2Hook.yml deleted file mode 100644 index 329120d..0000000 --- a/PD2Hook.yml +++ /dev/null @@ -1,17 +0,0 @@ -Config: - OutputMode: 1 - OutputFile: GoonBase.log - PrintOffsets: false - ExtraCols: 0 - ExtraRows: 0 - -KeyBindings: - -PersistScripts: - -PostRequireScripts: -# GOONBASE - - ["*", GoonBase/goonbase.lua] -# END - -# -- END OF FILE diff --git a/update_list.txt b/update_list.txt deleted file mode 100644 index f45a0d7..0000000 --- a/update_list.txt +++ /dev/null @@ -1,115 +0,0 @@ -GoonBase\lua\AchievementManager.lua -GoonBase\lua\ActionSpooc.lua -GoonBase\lua\AssetsTweakData.lua -GoonBase\lua\BlackMarketGUI.lua -GoonBase\lua\BlackMarketManager.lua -GoonBase\lua\BlackMarketTweakData.lua -GoonBase\lua\CharacterTweakData.lua -GoonBase\lua\ChatManager.lua -GoonBase\lua\ContourExt.lua -GoonBase\lua\CopActionHurt.lua -GoonBase\lua\CopDamage.lua -GoonBase\lua\CopInventory.lua -GoonBase\lua\CoreMenuInput.lua -GoonBase\lua\CoreMenuItem.lua -GoonBase\lua\CoreMenuItemSlider.lua -GoonBase\lua\CoreMenuLogic.lua -GoonBase\lua\CriminalsManager.lua -GoonBase\lua\ElementLaserTrigger.lua -GoonBase\lua\ElementSpawnGrenade.lua -GoonBase\lua\EnemyManager.lua -GoonBase\lua\FPCameraPlayerBase.lua -GoonBase\lua\FragGrenade.lua -GoonBase\lua\GageAssignmentManager.lua -GoonBase\lua\GameSetup.lua -GoonBase\lua\GameStateMachine.lua -GoonBase\lua\GroupAIManager.lua -GoonBase\lua\GroupAIStateBase.lua -GoonBase\lua\GroupAIStateBesiege.lua -GoonBase\lua\GroupAITweakData.lua -GoonBase\lua\HUDManager.lua -GoonBase\lua\HUDManagerPD2.lua -GoonBase\lua\InfamyTweakData.lua -GoonBase\lua\InteractionExt.lua -GoonBase\lua\JobManager.lua -GoonBase\lua\LevelsTweakData.lua -GoonBase\lua\LocalizationManager.lua -GoonBase\lua\MaskExt.lua -GoonBase\lua\MenuComponentManager.lua -GoonBase\lua\MenuItemCustomizeController.lua -GoonBase\lua\MenuManager.lua -GoonBase\lua\MenuNodeGUI.lua -GoonBase\lua\MenuSceneManager.lua -GoonBase\lua\MenuSetup.lua -GoonBase\lua\MissionBriefingGUI.lua -GoonBase\lua\NarrativeTweakData.lua -GoonBase\lua\NetworkGame.lua -GoonBase\lua\NetworkMatchMakingSteam.lua -GoonBase\lua\NewRaycastWeaponBase.lua -GoonBase\lua\NPCRaycastWeaponBase.lua -GoonBase\lua\PlayerDamage.lua -GoonBase\lua\PlayerInventory.lua -GoonBase\lua\PlayerManager.lua -GoonBase\lua\PlayerStandard.lua -GoonBase\lua\PreplanningTweakData.lua -GoonBase\lua\QuickSmokeGrenade.lua -GoonBase\lua\Setup.lua -GoonBase\lua\SpoocLogicAttack.lua -GoonBase\lua\TweakData.lua -GoonBase\lua\WeaponFlashlight.lua -GoonBase\lua\WeaponLaser.lua -GoonBase\lua\WeaponTweakData.lua -GoonBase\mods\colors\color_hsvrgb.lua -GoonBase\mods\colors\enemy_weapon_laser.lua -GoonBase\mods\colors\weapon_flashlight.lua -GoonBase\mods\colors\weapon_laser.lua -GoonBase\mods\colors\world_laser_colors.lua -GoonBase\mods\body_count.lua -GoonBase\mods\colour_grading.lua -GoonBase\mods\custom_grenades.lua -GoonBase\mods\custom_waypoints.lua -GoonBase\mods\extended_inventory.lua -GoonBase\mods\gage_coins.lua -GoonBase\mods\grenade_indicator.lua -GoonBase\mods\mod_shop.lua -GoonBase\mods\mutators.lua -GoonBase\mods\push_to_interact.lua -GoonBase\mods\trading.lua -GoonBase\mods\train_heist_plans.lua -GoonBase\mods\weapon_customization.lua -GoonBase\mods\weapon_customization_menus.lua -GoonBase\mods\weapon_customization_part_data.lua -GoonBase\mods\weapon_remember_gadget.lua -GoonBase\mods\zoom_sensitivity.lua -GoonBase\mutators\base_mutator.lua -GoonBase\mutators\mutator_addicts.lua -GoonBase\mutators\mutator_all_bulldozers.lua -GoonBase\mutators\mutator_all_cloakers.lua -GoonBase\mutators\mutator_all_shields.lua -GoonBase\mutators\mutator_all_tazers.lua -GoonBase\mutators\mutator_exploding_bullets.lua -GoonBase\mutators\mutator_floating_bodies.lua -GoonBase\mutators\mutator_insane_spawnrate.lua -GoonBase\mutators\mutator_insane_spawnrate_cops.lua -GoonBase\mutators\mutator_instagib.lua -GoonBase\mutators\mutator_jamming_weapons.lua -GoonBase\mutators\mutator_lightning_speed.lua -GoonBase\mutators\mutator_no_ammo_pickups.lua -GoonBase\mutators\mutator_realism_mode.lua -GoonBase\mutators\mutator_shielddozers.lua -GoonBase\mutators\mutator_suicidal_spawnrate.lua -GoonBase\mutators\mutator_suicidal_spawnrate_cops.lua -GoonBase\mutators\mutator_suicide_cloakers.lua -GoonBase\mutators\mutator_unbreakable.lua -GoonBase\req\autils.lua -GoonBase\req\hooks.lua -GoonBase\req\hooks_command_queue.lua -GoonBase\req\localization.lua -GoonBase\req\menus.lua -GoonBase\req\mods.lua -GoonBase\req\network.lua -GoonBase\req\options.lua -GoonBase\req\SimpleMenu.lua -GoonBase\req\updates.lua -GoonBase\goonbase.lua -PD2Hook.yml diff --git a/update_version.txt b/update_version.txt deleted file mode 100644 index e91fe90..0000000 --- a/update_version.txt +++ /dev/null @@ -1,4 +0,0 @@ -27 -1.24.2 -http://payday.jameswilko.com/archives/70 -manual=false From 621ebac40f2e25d06c5f2da2a1e77d2f23bab0f5 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Mon, 23 Feb 2015 11:42:29 +1100 Subject: [PATCH 02/92] Add development version --- GoonMod/goonbase.lua | 188 ++ GoonMod/loc/en.txt | 238 +++ GoonMod/lua/AchievementManager.lua | 56 + GoonMod/lua/ActionSpooc.lua | 14 + GoonMod/lua/AssetsTweakData.lua | 15 + GoonMod/lua/BlackMarketGUI.lua | 1629 +++++++++++++++++ GoonMod/lua/BlackMarketManager.lua | 393 ++++ GoonMod/lua/BlackMarketTweakData.lua | 8 + GoonMod/lua/CharacterTweakData.lua | 92 + GoonMod/lua/ChatManager.lua | 14 + GoonMod/lua/ContourExt.lua | 19 + GoonMod/lua/CopActionHurt.lua | 8 + GoonMod/lua/CopDamage.lua | 39 + GoonMod/lua/CopInventory.lua | 50 + GoonMod/lua/CoreMenuInput.lua | 40 + GoonMod/lua/CoreMenuItem.lua | 19 + GoonMod/lua/CoreMenuItemSlider.lua | 24 + GoonMod/lua/CoreMenuLogic.lua | 25 + GoonMod/lua/CoreMissionManager.lua | 108 ++ GoonMod/lua/CriminalsManager.lua | 12 + GoonMod/lua/ECMJammerBase.lua | 146 ++ GoonMod/lua/ElementLaserTrigger.lua | 14 + GoonMod/lua/ElementSpawnGrenade.lua | 6 + GoonMod/lua/EnemyManager.lua | 14 + GoonMod/lua/FPCameraPlayerBase.lua | 8 + GoonMod/lua/FragGrenade.lua | 14 + GoonMod/lua/GageAssignmentManager.lua | 8 + GoonMod/lua/GameSetup.lua | 8 + GoonMod/lua/GameStateMachine.lua | 8 + GoonMod/lua/GroupAIManager.lua | 7 + GoonMod/lua/GroupAIStateBase.lua | 15 + GoonMod/lua/GroupAIStateBesiege.lua | 529 ++++++ GoonMod/lua/GroupAITweakData.lua | 20 + GoonMod/lua/HUDManager.lua | 35 + GoonMod/lua/HUDManagerPD2.lua | 151 ++ GoonMod/lua/InfamyTweakData.lua | 6 + GoonMod/lua/InteractionExt.lua | 14 + GoonMod/lua/JobManager.lua | 8 + GoonMod/lua/LevelsTweakData.lua | 8 + GoonMod/lua/MaskExt.lua | 20 + GoonMod/lua/MenuComponentManager.lua | 8 + GoonMod/lua/MenuManager.lua | 83 + GoonMod/lua/MenuSceneManager.lua | 18 + GoonMod/lua/MenuSetup.lua | 14 + GoonMod/lua/MissionBriefingGUI.lua | 10 + GoonMod/lua/NPCRaycastWeaponBase.lua | 20 + GoonMod/lua/NarrativeTweakData.lua | 8 + GoonMod/lua/NetworkGame.lua | 21 + GoonMod/lua/NetworkMatchMakingSteam.lua | 17 + GoonMod/lua/NewRaycastWeaponBase.lua | 25 + GoonMod/lua/PlayerDamage.lua | 20 + GoonMod/lua/PlayerInventory.lua | 40 + GoonMod/lua/PlayerManager.lua | 17 + GoonMod/lua/PlayerStandard.lua | 38 + GoonMod/lua/PreplanningTweakData.lua | 35 + GoonMod/lua/QuickSmokeGrenade.lua | 14 + GoonMod/lua/Setup.lua | 8 + GoonMod/lua/SpoocLogicAttack.lua | 42 + GoonMod/lua/TweakData.lua | 9 + GoonMod/lua/WeaponFlashlight.lua | 46 + GoonMod/lua/WeaponLaser.lua | 37 + GoonMod/lua/WeaponTweakData.lua | 8 + GoonMod/menus/corpse_mod_menu.txt | 79 + GoonMod/menus/example_menu.txt | 68 + GoonMod/mod.txt | 67 + GoonMod/mods/body_count.lua | 95 + GoonMod/mods/disabled/colors/color_hsvrgb.lua | 216 +++ .../disabled/colors/enemy_weapon_laser.lua | 236 +++ .../disabled/colors/weapon_flashlight.lua | 194 ++ GoonMod/mods/disabled/colors/weapon_laser.lua | 376 ++++ .../disabled/colors/world_laser_colors.lua | 165 ++ GoonMod/mods/disabled/custom_waypoints.lua | 259 +++ GoonMod/mods/disabled/extended_inventory.lua | 324 ++++ GoonMod/mods/disabled/gage_coins.lua | 69 + GoonMod/mods/disabled/mod_shop.lua | 502 +++++ GoonMod/mods/disabled/mutators.lua | 857 +++++++++ GoonMod/mods/disabled/push_to_interact.lua | 231 +++ GoonMod/mods/disabled/stat_trak.lua | 372 ++++ .../disabled/stat_trak_weapon_offsets.lua | 62 + GoonMod/mods/disabled/trading.lua | 1497 +++++++++++++++ GoonMod/mods/disabled/train_heist_plans.lua | 111 ++ .../mods/disabled/weapon_customization.lua | 616 +++++++ .../disabled/weapon_customization_menus.lua | 756 ++++++++ .../weapon_customization_part_data.lua | 377 ++++ .../mods/disabled/weapon_remember_gadget.lua | 40 + GoonMod/mods/disabled/zoom_sensitivity.lua | 64 + GoonMod/mutators/base_mutator.lua | 249 +++ GoonMod/mutators/mutator_addicts.lua | 117 ++ GoonMod/mutators/mutator_all_bulldozers.lua | 157 ++ GoonMod/mutators/mutator_all_cloakers.lua | 158 ++ GoonMod/mutators/mutator_all_gangsters.lua | 73 + GoonMod/mutators/mutator_all_shields.lua | 157 ++ GoonMod/mutators/mutator_all_tazers.lua | 157 ++ .../mutators/mutator_exploding_bullets.lua | 60 + GoonMod/mutators/mutator_floating_bodies.lua | 76 + GoonMod/mutators/mutator_insane_spawnrate.lua | 450 +++++ .../mutator_insane_spawnrate_cops.lua | 293 +++ GoonMod/mutators/mutator_instagib.lua | 38 + GoonMod/mutators/mutator_jamming_weapons.lua | 37 + GoonMod/mutators/mutator_lightning_speed.lua | 130 ++ GoonMod/mutators/mutator_no_ammo_pickups.lua | 69 + GoonMod/mutators/mutator_realism_mode.lua | 51 + GoonMod/mutators/mutator_shielddozers.lua | 56 + .../mutators/mutator_suicidal_spawnrate.lua | 522 ++++++ .../mutator_suicidal_spawnrate_cops.lua | 373 ++++ GoonMod/mutators/mutator_suicide_cloakers.lua | 62 + GoonMod/mutators/mutator_tank_cloakers.lua | 26 + GoonMod/mutators/mutator_unbreakable.lua | 29 + GoonMod/req/autils.lua | 93 + GoonMod/req/localization.lua | 4 + GoonMod/req/mods.lua | 413 +++++ GoonMod/req/options.lua | 64 + 112 files changed, 16115 insertions(+) create mode 100644 GoonMod/goonbase.lua create mode 100644 GoonMod/loc/en.txt create mode 100644 GoonMod/lua/AchievementManager.lua create mode 100644 GoonMod/lua/ActionSpooc.lua create mode 100644 GoonMod/lua/AssetsTweakData.lua create mode 100644 GoonMod/lua/BlackMarketGUI.lua create mode 100644 GoonMod/lua/BlackMarketManager.lua create mode 100644 GoonMod/lua/BlackMarketTweakData.lua create mode 100644 GoonMod/lua/CharacterTweakData.lua create mode 100644 GoonMod/lua/ChatManager.lua create mode 100644 GoonMod/lua/ContourExt.lua create mode 100644 GoonMod/lua/CopActionHurt.lua create mode 100644 GoonMod/lua/CopDamage.lua create mode 100644 GoonMod/lua/CopInventory.lua create mode 100644 GoonMod/lua/CoreMenuInput.lua create mode 100644 GoonMod/lua/CoreMenuItem.lua create mode 100644 GoonMod/lua/CoreMenuItemSlider.lua create mode 100644 GoonMod/lua/CoreMenuLogic.lua create mode 100644 GoonMod/lua/CoreMissionManager.lua create mode 100644 GoonMod/lua/CriminalsManager.lua create mode 100644 GoonMod/lua/ECMJammerBase.lua create mode 100644 GoonMod/lua/ElementLaserTrigger.lua create mode 100644 GoonMod/lua/ElementSpawnGrenade.lua create mode 100644 GoonMod/lua/EnemyManager.lua create mode 100644 GoonMod/lua/FPCameraPlayerBase.lua create mode 100644 GoonMod/lua/FragGrenade.lua create mode 100644 GoonMod/lua/GageAssignmentManager.lua create mode 100644 GoonMod/lua/GameSetup.lua create mode 100644 GoonMod/lua/GameStateMachine.lua create mode 100644 GoonMod/lua/GroupAIManager.lua create mode 100644 GoonMod/lua/GroupAIStateBase.lua create mode 100644 GoonMod/lua/GroupAIStateBesiege.lua create mode 100644 GoonMod/lua/GroupAITweakData.lua create mode 100644 GoonMod/lua/HUDManager.lua create mode 100644 GoonMod/lua/HUDManagerPD2.lua create mode 100644 GoonMod/lua/InfamyTweakData.lua create mode 100644 GoonMod/lua/InteractionExt.lua create mode 100644 GoonMod/lua/JobManager.lua create mode 100644 GoonMod/lua/LevelsTweakData.lua create mode 100644 GoonMod/lua/MaskExt.lua create mode 100644 GoonMod/lua/MenuComponentManager.lua create mode 100644 GoonMod/lua/MenuManager.lua create mode 100644 GoonMod/lua/MenuSceneManager.lua create mode 100644 GoonMod/lua/MenuSetup.lua create mode 100644 GoonMod/lua/MissionBriefingGUI.lua create mode 100644 GoonMod/lua/NPCRaycastWeaponBase.lua create mode 100644 GoonMod/lua/NarrativeTweakData.lua create mode 100644 GoonMod/lua/NetworkGame.lua create mode 100644 GoonMod/lua/NetworkMatchMakingSteam.lua create mode 100644 GoonMod/lua/NewRaycastWeaponBase.lua create mode 100644 GoonMod/lua/PlayerDamage.lua create mode 100644 GoonMod/lua/PlayerInventory.lua create mode 100644 GoonMod/lua/PlayerManager.lua create mode 100644 GoonMod/lua/PlayerStandard.lua create mode 100644 GoonMod/lua/PreplanningTweakData.lua create mode 100644 GoonMod/lua/QuickSmokeGrenade.lua create mode 100644 GoonMod/lua/Setup.lua create mode 100644 GoonMod/lua/SpoocLogicAttack.lua create mode 100644 GoonMod/lua/TweakData.lua create mode 100644 GoonMod/lua/WeaponFlashlight.lua create mode 100644 GoonMod/lua/WeaponLaser.lua create mode 100644 GoonMod/lua/WeaponTweakData.lua create mode 100644 GoonMod/menus/corpse_mod_menu.txt create mode 100644 GoonMod/menus/example_menu.txt create mode 100644 GoonMod/mod.txt create mode 100644 GoonMod/mods/body_count.lua create mode 100644 GoonMod/mods/disabled/colors/color_hsvrgb.lua create mode 100644 GoonMod/mods/disabled/colors/enemy_weapon_laser.lua create mode 100644 GoonMod/mods/disabled/colors/weapon_flashlight.lua create mode 100644 GoonMod/mods/disabled/colors/weapon_laser.lua create mode 100644 GoonMod/mods/disabled/colors/world_laser_colors.lua create mode 100644 GoonMod/mods/disabled/custom_waypoints.lua create mode 100644 GoonMod/mods/disabled/extended_inventory.lua create mode 100644 GoonMod/mods/disabled/gage_coins.lua create mode 100644 GoonMod/mods/disabled/mod_shop.lua create mode 100644 GoonMod/mods/disabled/mutators.lua create mode 100644 GoonMod/mods/disabled/push_to_interact.lua create mode 100644 GoonMod/mods/disabled/stat_trak.lua create mode 100644 GoonMod/mods/disabled/stat_trak_weapon_offsets.lua create mode 100644 GoonMod/mods/disabled/trading.lua create mode 100644 GoonMod/mods/disabled/train_heist_plans.lua create mode 100644 GoonMod/mods/disabled/weapon_customization.lua create mode 100644 GoonMod/mods/disabled/weapon_customization_menus.lua create mode 100644 GoonMod/mods/disabled/weapon_customization_part_data.lua create mode 100644 GoonMod/mods/disabled/weapon_remember_gadget.lua create mode 100644 GoonMod/mods/disabled/zoom_sensitivity.lua create mode 100644 GoonMod/mutators/base_mutator.lua create mode 100644 GoonMod/mutators/mutator_addicts.lua create mode 100644 GoonMod/mutators/mutator_all_bulldozers.lua create mode 100644 GoonMod/mutators/mutator_all_cloakers.lua create mode 100644 GoonMod/mutators/mutator_all_gangsters.lua create mode 100644 GoonMod/mutators/mutator_all_shields.lua create mode 100644 GoonMod/mutators/mutator_all_tazers.lua create mode 100644 GoonMod/mutators/mutator_exploding_bullets.lua create mode 100644 GoonMod/mutators/mutator_floating_bodies.lua create mode 100644 GoonMod/mutators/mutator_insane_spawnrate.lua create mode 100644 GoonMod/mutators/mutator_insane_spawnrate_cops.lua create mode 100644 GoonMod/mutators/mutator_instagib.lua create mode 100644 GoonMod/mutators/mutator_jamming_weapons.lua create mode 100644 GoonMod/mutators/mutator_lightning_speed.lua create mode 100644 GoonMod/mutators/mutator_no_ammo_pickups.lua create mode 100644 GoonMod/mutators/mutator_realism_mode.lua create mode 100644 GoonMod/mutators/mutator_shielddozers.lua create mode 100644 GoonMod/mutators/mutator_suicidal_spawnrate.lua create mode 100644 GoonMod/mutators/mutator_suicidal_spawnrate_cops.lua create mode 100644 GoonMod/mutators/mutator_suicide_cloakers.lua create mode 100644 GoonMod/mutators/mutator_tank_cloakers.lua create mode 100644 GoonMod/mutators/mutator_unbreakable.lua create mode 100644 GoonMod/req/autils.lua create mode 100644 GoonMod/req/localization.lua create mode 100644 GoonMod/req/mods.lua create mode 100644 GoonMod/req/options.lua diff --git a/GoonMod/goonbase.lua b/GoonMod/goonbase.lua new file mode 100644 index 0000000..e172cec --- /dev/null +++ b/GoonMod/goonbase.lua @@ -0,0 +1,188 @@ + +if not _G.GoonBase then + _G.GoonBase = {} + GoonBase.Version = 26 + GoonBase.GameVersion = "1.24.3" + GoonBase.LogFile = "GoonMod.txt" + GoonBase.Path = "" + GoonBase.LuaPath = "lua/" + GoonBase.RequiresFolder = "req/" + GoonBase.ModsFolder = "mods/" + GoonBase.MenusPath = "menus/" + GoonBase.LocalizationFolder = "loc/" + GoonBase.SupportedVersion = true + GoonBase.LogTag = "[GoonMod]" +end + +GoonBase.RequireHookFiles = { + "lib/managers/menumanager", + "lib/setups/menusetup" +} + +GoonBase.HookFiles = { + + ["lib/managers/menumanager"] = "MenuManager.lua", + ["lib/managers/chatmanager"] = "ChatManager.lua", + ["lib/managers/enemymanager"] = "EnemyManager.lua", + ["lib/units/weapons/grenades/quicksmokegrenade"] = "QuickSmokeGrenade.lua", + ["lib/managers/hudmanager"] = "HUDManager.lua", + ["lib/managers/jobmanager"] = "JobManager.lua", + ["lib/managers/groupaimanager"] = "GroupAIManager.lua", + ["lib/managers/group_ai_states/groupaistatebase"] = "GroupAIStateBase.lua", + ["lib/managers/group_ai_states/groupaistatebesiege"] = "GroupAIStateBesiege.lua", + ["lib/units/beings/player/states/playerstandard"] = "PlayerStandard.lua", + ["lib/units/beings/player/playerdamage"] = "PlayerDamage.lua", + ["lib/managers/playermanager"] = "PlayerManager.lua", + ["lib/managers/gageassignmentmanager"] = "GageAssignmentManager.lua", + ["lib/managers/achievmentmanager"] = "AchievementManager.lua", + ["lib/tweak_data/infamytweakdata"] = "InfamyTweakData.lua", + ["lib/setups/gamesetup"] = "GameSetup.lua", + ["lib/setups/menusetup"] = "MenuSetup.lua", + ["lib/managers/menu/blackmarketgui"] = "BlackMarketGUI.lua", + ["lib/managers/blackmarketmanager"] = "BlackMarketManager.lua", + ["lib/tweak_data/groupaitweakdata"] = "GroupAITweakData.lua", + ["lib/tweak_data/charactertweakdata"] = "CharacterTweakData.lua", + ["lib/units/enemies/cop/copinventory"] = "CopInventory.lua", + ["lib/units/enemies/cop/copdamage"] = "CopDamage.lua", + ["lib/managers/mission/elementlasertrigger"] = "ElementLaserTrigger.lua", + ["lib/units/weapons/weaponflashlight"] = "WeaponFlashlight.lua", + ["lib/units/weapons/weaponlaser"] = "WeaponLaser.lua", + ["lib/tweak_data/levelstweakdata"] = "LevelsTweakData.lua", + ["lib/tweak_data/assetstweakdata"] = "AssetsTweakData.lua", + ["lib/tweak_data/narrativetweakdata"] = "NarrativeTweakData.lua", + ["lib/managers/criminalsmanager"] = "CriminalsManager.lua", + ["lib/units/weapons/newraycastweaponbase"] = "NewRaycastWeaponBase.lua", + ["lib/units/weapons/npcraycastweaponbase"] = "NPCRaycastWeaponBase.lua", + ["lib/units/cameras/fpcameraplayerbase"] = "FPCameraPlayerBase.lua", + ["core/lib/managers/menu/items/coremenuitemslider"] = "CoreMenuItemSlider.lua", + ["lib/utils/game_state_machine/gamestatemachine"] = "GameStateMachine.lua", + ["lib/units/contourext"] = "ContourExt.lua", + ["lib/units/interactions/interactionext"] = "InteractionExt.lua", + ["lib/units/enemies/spooc/actions/lower_body/actionspooc"] = "ActionSpooc.lua", + ["lib/managers/menu/menucomponentmanager"] = "MenuComponentManager.lua", + ["lib/managers/menu/missionbriefinggui"] = "MissionBriefingGUI.lua", + ["lib/network/matchmaking/networkmatchmakingsteam"] = "NetworkMatchMakingSteam.lua", + ["lib/managers/menu/menuscenemanager"] = "MenuSceneManager.lua", + ["lib/units/enemies/cop/actions/full_body/copactionhurt"] = "CopActionHurt.lua", + ["lib/units/equipment/ecm_jammer/ecmjammerbase"] = "ECMJammerBase.lua", + + -- ["lib/units/maskext"] = "MaskExt.lua", + -- ["lib/units/beings/player/playerinventory"] = "PlayerInventory.lua", + -- ["lib/network/networkgame"] = "NetworkGame.lua", + -- ["lib/setups/setup"] = "Setup.lua", + +} + +-- Required Global Functions +function _G.Print( ... ) + + local str = "" + for k, v in ipairs( arg ) do + str = GoonBase.LogTag .. str .. tostring(v) + end + + -- Write to console + log( str ) + + -- Write to log file + str = str .. "\n" + local file = io.open( GoonBase.LogFile, "a+" ) + if file ~= nil then + + io.output( file ) + + if GoonBase._print_cache ~= nil then + for k, v in ipairs(GoonBase._print_cache) do + io.write( v ) + end + GoonBase._print_cache = {} + end + io.write( str ) + + io.close( file ) + + else + + log( "[Error] Could not write to file, caching print string: '" .. str .. "'" ) + if GoonBase._print_cache == nil then + GoonBase._print_cache = {} + end + table.insert( GoonBase._print_cache, str ) + + end + +end + +function _G.SafeDoFile( fileName ) + + local success, errorMsg = pcall(function() + if io.file_is_readable( fileName ) then + dofile( fileName ) + else + Print("[Error] Could not open file '" .. fileName .. "'! Does it exist, is it readable?") + end + end) + + if not success then + Print("[Error]\nFile: " .. fileName .. "\n" .. errorMsg) + end + +end + +local unsupported = true + +-- Load Require and Mod Scripts +if not GoonBase.HasLoadedScripts then + + GoonBase.Path = ModPath + GoonBase.LogFile = LogsPath .. GoonBase.LogFile + GoonBase.MenusPath = ModPath .. GoonBase.MenusPath + + -- Check required classes exist now + if class and Application and string.split then + + GoonBase.HasLoadedScripts = true + + -- Load required files + local required_files = file.GetFiles( GoonBase.Path .. GoonBase.RequiresFolder ) + for k, v in ipairs( required_files ) do + SafeDoFile( GoonBase.Path .. GoonBase.RequiresFolder .. v ) + end + + -- Run hooks + if Hooks ~= nil then + + Hooks:RegisterHook("GoonBaseLoadMods") + Hooks:Call("GoonBaseLoadMods") + + Hooks:RegisterHook("GoonBasePostLoadedMods") + Hooks:Call("GoonBasePostLoadedMods") + + end + + end + +end + +-- Load Hook Scripts + +if RequiredScript then + + local requiredScript = RequiredScript:lower() + if GoonBase.HookFiles[requiredScript] then + + if GoonBase.SupportedVersion or (not GoonBase.SupportedVersion and table.contains(GoonBase.RequireHookFiles, requiredScript)) then + + if type( GoonBase.HookFiles[requiredScript] ) == "table" then + for k, v in pairs( GoonBase.HookFiles[requiredScript] ) do + SafeDoFile( GoonBase.Path .. GoonBase.LuaPath .. v ) + end + else + SafeDoFile( GoonBase.Path .. GoonBase.LuaPath .. GoonBase.HookFiles[requiredScript] ) + end + + end + + end + +end diff --git a/GoonMod/loc/en.txt b/GoonMod/loc/en.txt new file mode 100644 index 0000000..201f1cb --- /dev/null +++ b/GoonMod/loc/en.txt @@ -0,0 +1,238 @@ +{ + "GoonBaseOptionsName" : "GoonMod", + "GoonBaseOptionsDesc" : "Change your GoonMod preferences", + + "ModsMenu_Button" : "Modifications", + "ModsMenu_ButtonDesc" : "Control which modifications are loaded", + "ModsMenu_ButtonInfoButton" : "Help", + "ModsMenu_ButtonInfoButtonDesc" : "Show the modifications menu help", + "ModsMenu_ButtonInfoButtonTitle" : "Modifications", + "ModsMenu_ButtonInfoButtonMessage" : "This menu allows you to enable and disable specific modifications in GoonMod. If a modification is enabled, it will load itself when Payday 2 is launched.\nModifications highlighted in red require another modification to be enabled before they can be loaded, these modifications will be shown at the top of the screen.\nOnce a modification is enabled/disabled, you will be required to restart your game to ensure that the modifications fully and successfully load or unload.", + "ModsMenu_ButtonInfoButtonAccept" : "Close", + + "gm_options_corpse_menu_title" : "Corpses", + "gm_options_corpse_menu_desc" : "Change settings for the ingame corpses", + "gm_options_corpse_custom_title" : "Use Custom Corpse Amount", + "gm_options_corpse_custom_desc" : "Use the custom amount of corpses instead of the default amount (8)", + "gm_options_corpse_amount_title" : "Corpse Amount", + "gm_options_corpse_amount_desc" : "The maximum number of corpses allowed", + "gm_options_corpse_shields_title" : "Despawn Shields", + "gm_options_corpse_shields_desc" : "Despawn shields after a short time", + "gm_options_corpse_shields_timer_title" : "Shield Despawn Time", + "gm_options_corpse_shields_timer_desc" : "Despawn shields after a number of seconds", + "gm_options_corpse_keybind_remove_all_title" : "Remove All Corpses", + "gm_options_corpse_keybind_remove_all_desc" : "Key to clean up all corpses in the level when pressed", + "gm_options_corpse_keybind_remove_shields_title" : "Remove All Shields", + "gm_options_corpse_keybind_remove_shields_desc" : "Key to clean up all shields in the level when pressed", + + "OptionsMenu_CustomWaypointMenuTitle" : "Custom Waypoints", + "OptionsMenu_CustomWaypointMenuMessage" : "Change settings for your customizable waypoints", + "OptionsMenu_CustomWaypointKeybindPlace" : "Place Waypoint", + "OptionsMenu_CustomWaypointKeybindRemove" : "Remove Waypoint", + "OptionsMenu_CustomWaypointShowDistanceTitle" : "Show Distance on Waypoints", + "OptionsMenu_CustomWaypointShowDistanceMessage" : "Show how far away you are from custom waypoints", + + "bm_menu_extended_inv" : "Special", + "bm_ex_inv_in_reserve" : "IN RESERVE", + "bm_menu_amount_locked" : "NONE IN STOCK", + + "GageCoinName" : "Gage-Coin", + "GageCoinDesc" : "A single coin of an electronic cryptro-currency designed by Gage himself. When he launched it though, it fell flat and became worthless. However with the rise of Crime.net he's has been selling them to heisters at super inflated prices.\nGage gives one of these to every person who brings him a complete courier assignment.", + "GageCoinReserve" : " IN WALLET", + + "OptionsMenu_GrenadeMarker" : "Show Markers on Flashbangs", + "OptionsMenu_GrenadeMarkerDesc" : "Show a HUD marker when a flashbang is deployed", + + "ModShop_BlackmarketPurchaseWithGageCoins" : "Purchase with Gage Coins", + "ModShop_PurchaseWindowTitle" : "Purchase", + "ModShop_PurchaseWindowMessage" : "You are about to purchase {1}. This will cost you {2} Gage Coin/s.\n\nPurchasing:\n {1}, {2} GC/s\nBalance before purchase:\n {3} GC\nBalance after purchase:\n {4} GC", + "ModShop_PurchaseWindowAccept" : "Purchase", + "ModShop_PurchaseWindowCancel" : "Cancel", + "ModShop_FreeOfChargeTitle" : "Cannot Purchase", + "ModShop_FreeOfChargeMessage" : "{1} is free of charge and can be applied to as many weapons as you wish.", + "ModShop_FreeOfChargeAccept" : "OK", + "ModShop_NotEnoughCoinsWindowTitle" : "Cannot Purchase", + "ModShop_NotEnoughCoinsWindowMessage" : "You cannot purchase {1}, as you do not have enough Gage Coins to afford it. To purchase {1}, you need {2} GC/s.", + "ModShop_NotEnoughCoinsWindowAccept" : "OK", + + "Trading_OptionsMenuTitle" : "Crime.net Cargo", + "Trading_OptionsMenuMessage" : "Modify Crime.net Cargo Settings", + "Trading_OptionsTitle" : "Enable Crime.net Cargo", + "Trading_OptionsMessage" : "Enable Crime.net Cargo Modification", + "Trading_InventorySendToPlayer" : "Crime.net Cargo", + "Trading_TradeWindowTitle" : "Crime.net Cargo", + "Trading_TradeWindowMessage" : "Send {1} to:", + "Trading_TradeWindowCancel" : "Cancel", + "Trading_TradeWindowOk" : "OK", + "Trading_OnlineOnlyTitle" : "Crime.net Cargo Offline", + "Trading_OnlineOnlyMessage" : "You must be in a lobby to send cargo to somebody!", + "Trading_OnlineOnlyCancel" : "OK", + "Trading_NoPeersTitle" : "Crime.net Cargo", + "Trading_NoPeersMessage" : "There are no players who can receive cargo in the lobby!", + "Trading_NoPeersCancel" : "OK", + "Trading_TradeRequestMessage" : "{1} would like to send you {2} ({3}).", + "Trading_TradeRequestAccept" : "Accept", + "Trading_TradeRequestDecline" : "Decline", + "Trading_TradeAccepted" : "You have sent {1} to {2}!", + "Trading_TradeDeclined" : "{1} declined your {2}.", + "Trading_InventoryFull" : "{1} is trying to send you {2}, but you do not have any free {3} slots.", + "Trading_InventoryFullReason" : "{1} could not be sent to {2}, as they do not have enough free {3} slots.", + "Trading_WeaponModRemovedDLC" : "The {1} attached to this weapon has been removed as you do not own the '{2}' downloadable content.", + "Trading_WeaponSendWithMods" : "Do you want to send the mods attached to this weapon too?", + "Trading_WeaponSendMods" : "Send with mods", + "Trading_WeaponDontSendMods" : "Don't send mods", + "Trading_WeaponModReceived" : "You have received {1} from {2}.", + "Trading_WeaponModReceivedNoDLC" : "You have received {1} from {2}, but you do not own the '{3}' downloadable content and may not be able to use it.", + "Trading_WeaponModAccept" : "OK", + "Trading_MaskReceived" : "You have received {1} from {2}.", + "Trading_MaskReceivedNoDLC" : "You have received {1} from {2}, but you do not own the {3} downloadable content/s. Your items have been added to your stash.", + "Trading_MaskReceivedAccept" : "OK", + "Trading_UntradableMask" : "The mask '{1}' is untradable. {2} must be returned to the stash and can not be traded to other players.", + "Trading_UntradableMaskAccept" : "OK", + "Trading_UntradableMaskMod" : "The mask mod '{1}' is untradable, as it must be returned to the stash and can not be traded to other players.", + "Trading_UntradableMaskModAccept" : "OK", + "Trading_WaitingResponseTitle" : "Awaiting Response", + "Trading_WaitingResponseMessage" : "Awaiting response from {1}.", + "Trading_WaitingResponseCancel" : "Cancel Trade", + "Trading_OtherCancelledTitle" : "Trade Cancelled", + "Trading_OtherCancelledMessage" : "{1} has cancelled the trade with you.", + "Trading_OtherCancelledAccept" : "OK", + "Trading_AlreadyTradingTitle" : "Trade Unavailable", + "Trading_AlreadyTradingMessage" : "{1} is unavailable to trade with at the moment, as they are already in another trade.", + "Trading_AlreadyTradingAccept" : "OK", + + "menu_mutators" : "Mutators", + "Mutators_OptionsName" : "Mutators", + "Mutators_OptionsDesc" : "Control active gameplay Mutators", + "Mutators_OptionsIngameName" : "Mutators", + "Mutators_OptionsIngameDesc" : "Control active gameplay Mutators (Changes will take place on a restart/new day)", + "Mutators_IncompatibleTitle" : "Mutators", + "Mutators_IncompatibleMessage" : "'{1}' could not be enabled as it is in compatible with {2}. Please disable these mutators first.", + "Mutators_IncompatibleAccept" : "OK", + "Mutators_HelpButton" : "Help", + "Mutators_HelpButtonDesc" : "Show the mutators menu help", + "Mutators_HelpTitle" : "Mutators", + "Mutators_HelpMessage" : "This menu allows you to enable and disable specific gameplay mutators. Mutators are small gameplay modifications that can offer new gameplay modes, and unique experiences.\n\nCertain mutators may be incompatible with other mutators, and will turn red when an incompatible mutator is active. In order to use this mutator, you must first disable the incompatible mutator.\n\nMutators are only active in certain circumstances, and in order to prevent griefing in public games, mutators are disabled in all games except for friends-only, and private games.\n\nMutators will also disable ALL achievements while they are active. If you are achievement hunting, disable all of your mutators first.", + "Mutators_HelpAccept" : "Close", + "Randomizer_Name" : "Randomizer", + "Randomizer_Desc" : "Randomly selects mutations that are compatible with each other to be activated at the start of every heist.\nAny mutations that you have turned on will also be enabled.", + "Randomizer_Off" : "Off", + "Randomizer_UpTo1" : "Single Mutation", + "Randomizer_UpTo2" : "Up to 2 Mutations", + "Randomizer_UpTo3" : "Up to 3 Mutations", + "Randomizer_UpTo4" : "Up to 4 Mutations", + "Randomizer_UpTo5" : "Up to 5 Mutations", + "Mutators_PublicGamesWarning_Title" : "Public Lobby Disabled", + "Mutators_PublicGamesWarning_Message" : "Cannot make lobby public as mutators are active. If you wish to host a public game, please disable your mutators first.", + "Mutators_PublicGamesWarning_MessageIngame" : "Cannot make lobby public as mutators have been activated. If you wish to host a public game, please return to the lobby and disable your mutators.", + "Mutators_PublicGamesWarning_Cancel" : "OK", + "NetworkedMutators_SendingData_Title" : "Sending Mutators", + "NetworkedMutators_SendingData_Message" : "Sending mutator data to other players, please wait...", + "NetworkedMutators_SendingData_Cancel" : "Cancel", + "NetworkedMutators_SendingData_DebugForce" : "Force Start", + "NetworkedMutators_SendingData_DebugRelease" : "Release Start Delay", + "BriefingMenu_ActiveMutators" : "Active Mutations", + "MissingMutators_Title" : "Missing Mutators", + "MissingMutators_Message" : "Some players in the lobby are missing mutators you are trying to activate, they have been listed below.", + "MissingMutators_Continue" : "Continue Anyway", + "MissingMutators_Cancel" : "Cancel", + + "OptionsMenu_PushInteractSubmenuTitle" : "Push to Interact", + "OptionsMenu_PushInteractSubmenuDesc" : "Change settings for Push to Interact", + "OptionsMenu_PushInteractEnableTitle" : "Enable Push to Interact", + "OptionsMenu_PushInteractEnableDesc" : "Enable Push to Interact, pushing the interact button will automatically hold the button until it is pushed again", + "OptionsMenu_PushInteractTimeTitle" : "Push Grace Period", + "OptionsMenu_PushInteractTimeDesc" : "Grace period of the push in seconds. Push-to-interact will only take effect if the button is held for this long", + "OptionsMenu_PushInteractHelperTitle" : "Enable Helper Indicator", + "OptionsMenu_PushInteractHelperDesc" : "Show a blue outline around the interaction timer when Push-to-interact will hold the interaction for you", + + "OptionsMenu_StatTrakSubmenuTitle" : "Stat-trak Weapons", + "OptionsMenu_StatTrakSubmenuDesc" : "Weapons will track kills made with them", + "OptionsMenu_InspectWeapon" : "Inspect Weapon", + "OptionsMenu_InspectWeaponDesc" : "Key to press to inspect your weapon while in-game", + "OptionsMenu_CycleStattrakMode" : "Cycle Stat-trak Modes", + "OptionsMenu_CycleStattrakModeDesc" : "Key to cycle through available kill modes on your weapons", + + "TrainHeist_PlansInv" : "Train Intel", + "TrainHeist_PlansInvDesc" : "Details of a train transporting an experimental turret. This unlocks the Train Transport heist in Crime.net while you have intel in reserve.\n\nFound in an Armoured Transport. Will be consumed upon successful completion of the heist.", + + "OptionsMenu_ZoomSensitivityTitle" : "Enabled Ironsight Normalized Sensitivity", + "OptionsMenu_ZoomSensitivityMessage" : "Lower the sensitivity when using ironsights to more accurately place your shots.", + + "Options_WeaponCustomizationName" : "Weapon Customization", + "Options_WeaponCustomizationDesc" : "Weapon Customization Options", + "WeaponCustomization_DownloadModOverridesManual" : "Download Mod Overrides (Manual Install)", + "WeaponCustomization_DownloadModOverridesManualDesc" : "Download the required mod overrides to use the Weapon Customization mod, opens in your browser.\nPlace the 'GoonModWeaponCustomizer' folder into your mod_overrides and then restart your game.", + "WeaponCustomization_ClearDataButton" : "Clear Weapon Customization Data", + "WeaponCustomization_ClearDataButtonDesc" : "Erase all of your weapon customization data from your Payday 2 save file", + "WeaponCustomization_ClearDataTitle" : "Clear Weapon Customization Data", + "WeaponCustomization_ClearDataMessage" : "This will erase all weapon customization data from your save game.\nYou will lose all of your modified weapon visuals, but you will not lose the weapons themselves.\n\nYou may need to restart your game, or load a mission, to fully clear your texture cache.", + "WeaponCustomization_ClearDataAccept" : "Clear Data", + "WeaponCustomization_ClearDataCancel" : "Cancel", + "WeaponCustomization_PrintAllPartNames" : "Output All Weapon Part Names", + "WeaponCustomization_PrintAllPartNamesDesc" : "Outputs all weapon part names to a CSV file", + "bm_mtl_no_material" : "No Material", + "WeaponCustomization_MenuItem" : "Customize Weapon", + "bm_menu_customize_weapon_title" : "Customize Weapon: $weapon_name", + "wc_modifying_parts" : "Modifying Parts", + "wc_not_modifying_parts" : "Not Modifying Parts", + "wc_highlighted_mod" : "Highlighted", + "wc_unavailable_mod" : "Unavailable", + "wc_advanced_options" : "Advanced", + "wc_advanced_options_menu" : "Advanced Options", + "wc_adv_clear_weapon" : "Revert to Factory Standard", + "wc_adv_toggle_preview_spin" : "Toggle Rotating Preview", + "wc_adv_toggle_colour_grading" : "Toggle Ingame Colour Grading", + "wc_clear_weapon_title" : "Revert Weapon", + "wc_clear_weapon_message" : "This will revert your weapon customization back to factory standard, do you wish to continue?", + "wc_clear_weapon_accept" : "Revert", + "wc_clear_weapon_cancel" : "Cancel", + "wc_mod_overrides_not_installed_title" : "Mod Overrides Missing", + "wc_mod_overrides_not_installed_desc" : "The mod overrides required to make the Weapon Customization mod are missing from your game mod_overrides folder.\nYou must install these, or else you will not see your weapon customization.\n\nA link to the download is found below. Extract the inner folder 'GoonModWeaponCustomizer' to your mod_overrides folder in your Payday 2 assets folder, and then restart your game.", + "wc_mod_overrides_not_installed_download" : "Download Now", + "wc_mod_overrides_not_installed_dont_show" : "I Know, Don't Show This Again", + "wc_mod_overrides_not_installed_cancel" : "Later", + + "Options_EnemyLaserName" : "Enemy Laser Color", + "Options_EnemyLaserDesc" : "Modify the color of enemy's weapons lasers", + "Options_EnemyLaserEnableTitle" : "Enable Custom Enemy Laser Color", + "Options_EnemyLaserEnableDesc" : "Use the custom set color for enemy's weapons Lasers", + "Options_EnemyLaserRainbowTitle" : "Enable Rainbow Laser", + "Options_EnemyLaserRainbowDesc" : "Enable rainbow instead of the set Hue", + "Options_EnemyLaserRainbowSpeedTitle" : "Rainbow Speed", + "Options_EnemyLaserRainbowSpeedDesc" : "Set the speed of the rainbow effect", + + "Options_WeaponLightName" : "Weapon Flashlight Color", + "Options_WeaponLightDesc" : "Modify the color of weapon flashlights", + "Options_WeaponLightEnableTitle" : "Enable Custom Weapon Flashlight Color", + "Options_WeaponLightEnableDesc" : "Use the custom set color for Weapon Flashlights", + "Options_WeaponLightRainbowTitle" : "Enable Rainbow Light", + "Options_WeaponLightRainbowDesc" : "Enable rainbow instead of the set Hue", + "Options_WeaponLightRainbowSpeedTitle" : "Rainbow Speed", + "Options_WeaponLightRainbowSpeedDesc" : "Set the speed of the rainbow effect", + + "Options_WeaponLaserName" : "Weapon Laser Color", + "Options_WeaponLaserDesc" : "Modify the color of weapon lasers", + "Options_WeaponLaserEnableTitle" : "Enable Custom Weapon Laser Color", + "Options_WeaponLaserEnableDesc" : "Use the custom set color for Weapon Lasers", + "Options_WeaponLaserRainbowTitle" : "Enable Rainbow Laser", + "Options_WeaponLaserRainbowDesc" : "Enable rainbow instead of the set Hue", + "Options_WeaponLaserRainbowSpeedTitle" : "Rainbow Speed", + "Options_WeaponLaserRainbowSpeedDesc" : "Set the speed of the rainbow effect", + "Options_TeammateLaserOption" : "Teammate Lasers", + "Options_TeammateLaserOptionDesc" : "Set how teammate lasers should appear", + "Options_TeammateLaser_Same" : "Use My Colour", + "Options_TeammateLaser_Theirs" : "Use Their Colour", + "Options_TeammateLaser_Unique" : "Unique per Person", + + "Options_WorldLaserName" : "World Laser Color", + "Options_WorldLaserDesc" : "Modify the color of lasers that appear in the world", + "Options_WorldLaserEnableTitle" : "Enable Custom World Laser Color", + "Options_WorldLaserEnableDesc" : "Use the custom set color for World Lasers", + "Options_WorldLaserRainbowTitle" : "Enable Rainbow Laser", + "Options_WorldLaserRainbowDesc" : "Enable rainbow instead of the set Hue", + "Options_WorldLaserRainbowSpeedTitle" : "Rainbow Speed", + "Options_WorldLaserRainbowSpeedDesc" : "Set the speed of the rainbow effect", + + "aaaaaaa" : "aaaaaaaaa", +} diff --git a/GoonMod/lua/AchievementManager.lua b/GoonMod/lua/AchievementManager.lua new file mode 100644 index 0000000..c399101 --- /dev/null +++ b/GoonMod/lua/AchievementManager.lua @@ -0,0 +1,56 @@ + +CloneClass( AchievmentManager ) + +AchievmentManager._disabled_stack = {} + +Hooks:RegisterHook("AchievementManagerPreAward") +function AchievmentManager.award(self, id) + + Hooks:Call("AchievementManagerPreAward", self, id) + + AchievmentManager:CheckAchievementsDisabled() + if self:AchievementsDisabled() then + return + end + + self.orig.award(self, id) + +end + +Hooks:RegisterHook("AchievementManagerPreAwardProgress") +function AchievmentManager.award_progress(self, stat, value) + + Hooks:Call("AchievementManagerPreAwardProgress", self, stat, value) + + AchievmentManager:CheckAchievementsDisabled() + if self:AchievementsDisabled() then + return + end + + self.orig.award_progress(self, stat, value) + +end + +function AchievmentManager:AchievementsDisabled() + local disabled = false + for k, v in pairs( self._disabled_stack ) do + if v ~= nil and v == true then + disabled = true + end + end + return disabled +end + +function AchievmentManager:DisableAchievements(id) + self._disabled_stack[id] = true +end + +function AchievmentManager:EnableAchievements(id) + self._disabled_stack[id] = nil +end + +Hooks:RegisterHook("AchievementManagerCheckDisable") +function AchievmentManager:CheckAchievementsDisabled() + AchievmentManager._disabled_stack = {} + Hooks:Call("AchievementManagerCheckDisable", self) +end diff --git a/GoonMod/lua/ActionSpooc.lua b/GoonMod/lua/ActionSpooc.lua new file mode 100644 index 0000000..fad9540 --- /dev/null +++ b/GoonMod/lua/ActionSpooc.lua @@ -0,0 +1,14 @@ + +CloneClass( ActionSpooc ) + +Hooks:RegisterHook("ActionSpoocInitialize") +function ActionSpooc.init(self, action_desc, common_data) + Hooks:Call("ActionSpoocInitialize", self, action_desc, common_data) + return self.orig.init(self, action_desc, common_data) +end + +Hooks:RegisterHook("ActionSpoocAnimActCallback") +function ActionSpooc.anim_act_clbk(self, anim_act) + Hooks:Call("ActionSpoocAnimActCallback", self, anim_act) + return self.orig.anim_act_clbk(self, anim_act) +end diff --git a/GoonMod/lua/AssetsTweakData.lua b/GoonMod/lua/AssetsTweakData.lua new file mode 100644 index 0000000..2651e85 --- /dev/null +++ b/GoonMod/lua/AssetsTweakData.lua @@ -0,0 +1,15 @@ + +CloneClass( AssetsTweakData ) + +function AssetsTweakData._init_assets(self, tweak_data) + + self.orig._init_assets(self, tweak_data) + + table.insert(self.bodybags_bag.stages, "arm_for_prof") + table.insert(self.grenade_crate.stages, "arm_for_prof") + + table.insert(self.arm_for_info.stages, "arm_for_prof") + table.insert(self.arm_for_ammo.stages, "arm_for_prof") + table.insert(self.arm_for_health.stages, "arm_for_prof") + +end diff --git a/GoonMod/lua/BlackMarketGUI.lua b/GoonMod/lua/BlackMarketGUI.lua new file mode 100644 index 0000000..1d90a2b --- /dev/null +++ b/GoonMod/lua/BlackMarketGUI.lua @@ -0,0 +1,1629 @@ + +CloneClass( BlackMarketGui ) + +local is_win32 = SystemInfo:platform() == Idstring("WIN32") +local NOT_WIN_32 = not is_win32 +local WIDTH_MULTIPLIER = NOT_WIN_32 and 0.68 or 0.71 +local BOX_GAP = 13.5 +local GRID_H_MUL = (NOT_WIN_32 and 7 or 6.6) / 8 +local massive_font = tweak_data.menu.pd2_massive_font +local large_font = tweak_data.menu.pd2_large_font +local medium_font = tweak_data.menu.pd2_medium_font +local small_font = tweak_data.menu.pd2_small_font +local massive_font_size = tweak_data.menu.pd2_massive_font_size +local large_font_size = tweak_data.menu.pd2_large_font_size +local medium_font_size = tweak_data.menu.pd2_medium_font_size +local small_font_size = tweak_data.menu.pd2_small_font_size + +function BlackMarketGui.init(self, ws, fullscreen_ws, node) + + self._ws = ws + self._fullscreen_ws = fullscreen_ws + self._init_layer = self._ws:panel():layer() + self._node = node + local component_data = self._node:parameters().menu_component_data + + -- Fix for custom tabs crashing inventory + if node._parameters.name == "blackmarket" then + component_data = nil + end + + local do_animation = not component_data and not self._data + local is_start_page = not component_data and true or false + + managers.menu_component:close_contract_gui() + self:_setup(is_start_page, component_data) + if do_animation then + local fade_me_in_scotty = function(o) + over(0.1, function(p) + o:set_alpha(p) + end +) + end + + self._panel:animate(fade_me_in_scotty) + self._fullscreen_panel:animate(fade_me_in_scotty) + end + self:set_enabled(true) + +end + +Hooks:RegisterHook("BlackMarketGUIPreSetup") +Hooks:RegisterHook("BlackMarketGUIPostSetup") +function BlackMarketGui._setup(self, is_start_page, component_data) + + local psuccess, perror = pcall(function() + + Hooks:Call("BlackMarketGUIPreSetup", self, is_start_page, component_data) + self.orig._setup(self, is_start_page, component_data) + Hooks:Call("BlackMarketGUIPostSetup", self, is_start_page, component_data) + self:on_slot_selected( self._selected_slot ) + + end) + if not psuccess then + Print("[Error] " .. perror) + end + +end + +Hooks:RegisterHook("BlackMarketGUIOnPopulateWeapons") +Hooks:RegisterHook("BlackMarketGUIOnPopulateWeaponActionList") +function BlackMarketGui.populate_weapon_category(self, category, data) + + Hooks:Call("BlackMarketGUIOnPopulateWeapons", self, category, data) + + local crafted_category = managers.blackmarket:get_crafted_category(category) or {} + local last_weapon = table.size(crafted_category) == 1 + local last_unlocked_weapon + if not last_weapon then + + local category_size = table.size(crafted_category) + for i, crafted in pairs(crafted_category) do + if not managers.blackmarket:weapon_unlocked(crafted.weapon_id) then + category_size = category_size - 1 + end + end + + last_unlocked_weapon = category_size == 1 + + end + + local hold_crafted_item = managers.blackmarket:get_hold_crafted_item() + local currently_holding = hold_crafted_item and hold_crafted_item.category == category + local max_items = data.override_slots and data.override_slots[1] * data.override_slots[2] or 9 + local max_rows = tweak_data.gui.MAX_WEAPON_ROWS or 3 + + max_items = max_rows * (data.override_slots and data.override_slots[2] or 3) + for i = 1, max_items do + data[i] = nil + end + + local guis_catalog = "guis/" + local weapon_data = Global.blackmarket_manager.weapons + local new_data = {} + local index = 0 + + for i, crafted in pairs(crafted_category) do + + guis_catalog = "guis/" + local bundle_folder = tweak_data.weapon[crafted.weapon_id] and tweak_data.weapon[crafted.weapon_id].texture_bundle_folder + if bundle_folder then + guis_catalog = guis_catalog .. "dlcs/" .. tostring(bundle_folder) .. "/" + end + + new_data = {} + new_data.name = crafted.weapon_id + new_data.name_localized = managers.blackmarket:get_weapon_name_by_category_slot(category, i) + new_data.raw_name_localized = managers.weapon_factory:get_weapon_name_by_factory_id(crafted.factory_id) + new_data.custom_name_text = managers.blackmarket:get_crafted_custom_name(category, i, true) + new_data.category = category + new_data.slot = i + new_data.unlocked = managers.blackmarket:weapon_unlocked(crafted.weapon_id) + new_data.level = managers.blackmarket:weapon_level(crafted.weapon_id) + new_data.can_afford = true + new_data.equipped = crafted.equipped + new_data.skill_based = weapon_data[crafted.weapon_id].skill_based + new_data.skill_name = new_data.skill_based and "bm_menu_skill_locked_" .. new_data.name + new_data.price = managers.money:get_weapon_slot_sell_value(category, i) + local texture_name = tweak_data.weapon[crafted.weapon_id].texture_name or tostring(crafted.weapon_id) + new_data.bitmap_texture = guis_catalog .. "textures/pd2/blackmarket/icons/weapons/" .. texture_name + new_data.comparision_data = new_data.unlocked and managers.blackmarket:get_weapon_stats(category, i) + new_data.global_value = tweak_data.weapon[new_data.name] and tweak_data.weapon[new_data.name].global_value or "normal" + new_data.dlc_locked = tweak_data.lootdrop.global_values[new_data.global_value].unlock_id or nil + new_data.lock_texture = self:get_lock_icon(new_data) + new_data.holding = currently_holding and hold_crafted_item.slot == i + + local icon_list = managers.menu_component:create_weapon_mod_icon_list(crafted.weapon_id, category, crafted.factory_id, i) + local icon_index = 1 + local new_parts = {} + for k, part in pairs(managers.blackmarket:get_weapon_new_part_drops(crafted.factory_id) or {}) do + local type = tweak_data.weapon.factory.parts[part].type + new_parts[type] = true + end + + if table.size(new_parts) > 0 then + new_data.new_drop_data = {} + end + + new_data.mini_icons = {} + for k, icon in pairs(icon_list) do + table.insert(new_data.mini_icons, { + texture = icon.texture, + right = (icon_index - 1) * 18, + bottom = 0, + layer = 1, + w = 16, + h = 16, + stream = false, + alpha = icon.equipped and 1 or 0.25 + }) + if new_parts[icon.type] then + table.insert(new_data.mini_icons, { + texture = "guis/textures/pd2/blackmarket/inv_mod_new", + right = (icon_index - 1) * 18, + bottom = 16, + layer = 1, + w = 16, + h = 8, + stream = false, + alpha = 1 + }) + end + + icon_index = icon_index + 1 + end + + if not new_data.unlocked then + new_data.last_weapon = last_weapon + else + new_data.last_weapon = last_weapon or last_unlocked_weapon + end + + if new_data.equipped then + self._equipped_comparision_data = self._equipped_comparision_data or {} + self._equipped_comparision_data[category] = new_data.comparision_data + end + + if currently_holding then + new_data.selected_text = managers.localization:to_upper_text("bm_menu_btn_swap_weapon") + if new_data.slot ~= hold_crafted_item.slot then + table.insert(new_data, "w_swap") + end + + table.insert(new_data, "i_stop_move") + else + local has_mods = managers.weapon_factory:has_weapon_more_than_default_parts(crafted.factory_id) + if has_mods and new_data.unlocked then + table.insert(new_data, "w_mod") + end + + if not new_data.last_weapon then + table.insert(new_data, "w_sell") + end + + if not new_data.equipped and new_data.unlocked then + table.insert(new_data, "w_equip") + end + + if new_data.equipped and new_data.unlocked then + table.insert(new_data, "w_move") + end + + table.insert(new_data, "w_preview") + + Hooks:Call("BlackMarketGUIOnPopulateWeaponActionList", self, new_data) + + end + + data[i] = new_data + index = i + end + + for i = 1, max_items do + if not data[i] then + local can_buy_weapon = managers.blackmarket:is_weapon_slot_unlocked(category, i) + new_data = {} + if can_buy_weapon then + + new_data.name = "bm_menu_btn_buy_new_weapon" + new_data.name_localized = managers.localization:text("bm_menu_empty_weapon_slot") + new_data.mid_text = {} + new_data.mid_text.noselected_text = new_data.name_localized + new_data.mid_text.noselected_color = tweak_data.screen_colors.button_stage_3 + + if not currently_holding or not new_data.mid_text.noselected_text then + end + + new_data.mid_text.selected_text = managers.localization:text("bm_menu_btn_buy_new_weapon") + new_data.mid_text.selected_color = currently_holding and new_data.mid_text.noselected_color or tweak_data.screen_colors.button_stage_2 + new_data.empty_slot = true + new_data.category = category + new_data.slot = i + new_data.unlocked = true + new_data.can_afford = true + new_data.equipped = false + + if currently_holding then + new_data.selected_text = managers.localization:to_upper_text("bm_menu_btn_place_weapon") + table.insert(new_data, "w_place") + table.insert(new_data, "i_stop_move") + else + table.insert(new_data, "ew_buy") + end + + if managers.blackmarket:got_new_drop(new_data.category, "weapon_buy_empty", nil) then + new_data.mini_icons = new_data.mini_icons or {} + table.insert(new_data.mini_icons, { + name = "new_drop", + texture = "guis/textures/pd2/blackmarket/inv_newdrop", + right = 0, + top = 0, + layer = 1, + w = 16, + h = 16, + stream = false, + visible = false + }) + new_data.new_drop_data = {} + end + + else + + new_data.name = "bm_menu_btn_buy_weapon_slot" + new_data.name_localized = managers.localization:text("bm_menu_locked_weapon_slot") + new_data.empty_slot = true + new_data.category = category + new_data.slot = i + new_data.unlocked = true + new_data.equipped = false + new_data.lock_texture = "guis/textures/pd2/blackmarket/money_lock" + new_data.lock_color = tweak_data.screen_colors.button_stage_3 + new_data.lock_shape = { + w = 32, + h = 32, + x = 0, + y = -32 + } + new_data.locked_slot = true + new_data.dlc_locked = managers.experience:cash_string(managers.money:get_buy_weapon_slot_price()) + new_data.mid_text = {} + new_data.mid_text.noselected_text = new_data.name_localized + new_data.mid_text.noselected_color = tweak_data.screen_colors.button_stage_3 + new_data.mid_text.is_lock_same_color = true + + if currently_holding then + new_data.mid_text.selected_text = new_data.mid_text.noselected_text + new_data.mid_text.selected_color = new_data.mid_text.noselected_color + table.insert(new_data, "i_stop_move") + elseif managers.money:can_afford_buy_weapon_slot() then + new_data.mid_text.selected_text = managers.localization:text("bm_menu_btn_buy_weapon_slot") + new_data.mid_text.selected_color = tweak_data.screen_colors.button_stage_2 + table.insert(new_data, "ew_unlock") + else + new_data.mid_text.selected_text = managers.localization:text("bm_menu_cannot_buy_weapon_slot") + new_data.mid_text.selected_color = tweak_data.screen_colors.important_1 + new_data.dlc_locked = new_data.dlc_locked .. " " .. managers.localization:to_upper_text("bm_menu_cannot_buy_weapon_slot") + new_data.mid_text.lock_noselected_color = tweak_data.screen_colors.important_1 + new_data.cannot_buy = true + end + + end + + data[i] = new_data + end + + end + +end + +Hooks:RegisterHook("BlackMarketGUIOnPopulateMeleeWeapons") +Hooks:RegisterHook("BlackMarketGUIOnPopulateMeleeWeaponActionList") +function BlackMarketGui.populate_melee_weapons(self, data) + + Hooks:Call("BlackMarketGUIOnPopulateMeleeWeapons", self, data) + + local new_data = {} + local sort_data = {} + local xd, yd, x_td, y_td, x_sn, y_sn, x_gv, y_gv + local m_tweak_data = tweak_data.blackmarket.melee_weapons + local l_tweak_data = tweak_data.lootdrop.global_values + local global_value + + for id, d in pairs(Global.blackmarket_manager.melee_weapons) do + global_value = tweak_data.blackmarket.melee_weapons[id].dlc or tweak_data.blackmarket.melee_weapons[id].global_value or "normal" + if d.unlocked or d.equipped or not tweak_data:get_raw_value("lootdrop", "global_values", global_value, "hide_unavailable") then + table.insert(sort_data, {id, d}) + end + end + + table.sort(sort_data, function(x, y) + + xd = x[2] + yd = y[2] + x_td = m_tweak_data[x[1]] + y_td = m_tweak_data[y[1]] + + if not xd.is_favorite ~= not yd.is_favorite then + return xd.is_favorite + end + if xd.unlocked ~= yd.unlocked then + return xd.unlocked + end + if x_td.instant ~= y_td.instant then + return x_td.instant + end + if xd.skill_based ~= yd.skill_based then + return xd.skill_based + end + if x_td.free ~= y_td.free then + return x_td.free + end + + x_gv = x_td.global_value or x_td.dlc or "normal" + y_gv = y_td.global_value or y_td.dlc or "normal" + x_sn = l_tweak_data[x_gv] + y_sn = l_tweak_data[y_gv] + x_sn = x_sn and x_sn.sort_number or 1 + y_sn = y_sn and y_sn.sort_number or 1 + + if x_sn ~= y_sn then + return x_sn < y_sn + end + if xd.level ~= yd.level then + return xd.level < yd.level + end + + return x[1] < y[1] + + end) + + local max_items = math.ceil(#sort_data / (data.override_slots[1] or 3)) * (data.override_slots[1] or 3) + local index = 0 + local guis_catalog, m_tweak_data, melee_weapon_id + + for i = 1, max_items do + data[i] = nil + end + + for i, melee_weapon_data in ipairs(sort_data) do + + melee_weapon_id = melee_weapon_data[1] + m_tweak_data = tweak_data.blackmarket.melee_weapons[melee_weapon_data[1]] or {} + guis_catalog = "guis/" + + local bundle_folder = m_tweak_data.texture_bundle_folder + if bundle_folder then + guis_catalog = guis_catalog .. "dlcs/" .. tostring(bundle_folder) .. "/" + end + + new_data = {} + new_data.name = melee_weapon_id + new_data.name_localized = managers.localization:text(tweak_data.blackmarket.melee_weapons[new_data.name].name_id) + new_data.category = "melee_weapons" + new_data.slot = i + new_data.unlocked = melee_weapon_data[2].unlocked + new_data.equipped = melee_weapon_data[2].equipped + new_data.level = melee_weapon_data[2].level + new_data.stream = true + new_data.global_value = m_tweak_data.dlc or "normal" + new_data.skill_based = melee_weapon_data[2].skill_based + new_data.skill_name = "bm_menu_skill_locked_" .. new_data.name + + if m_tweak_data and m_tweak_data.locks then + + local dlc = m_tweak_data.locks.dlc + local achievement = m_tweak_data.locks.achievement + local saved_job_value = m_tweak_data.locks.saved_job_value + local level = m_tweak_data.locks.level + new_data.dlc_based = true + new_data.lock_texture = self:get_lock_icon(new_data, "guis/textures/pd2/lock_community") + if achievement and managers.achievment:get_info(achievement) and not managers.achievment:get_info(achievement).awarded then + new_data.dlc_locked = "menu_bm_achievement_locked_" .. tostring(achievement) + elseif dlc and not managers.dlc:is_dlc_unlocked(dlc) then + new_data.dlc_locked = tweak_data.lootdrop.global_values[dlc] and tweak_data.lootdrop.global_values[dlc].unlock_id or "bm_menu_dlc_locked" + else + new_data.dlc_locked = tweak_data.lootdrop.global_values[new_data.global_value].unlock_id or "bm_menu_dlc_locked" + end + + else + new_data.lock_texture = self:get_lock_icon(new_data) + new_data.dlc_locked = tweak_data.lootdrop.global_values[new_data.global_value].unlock_id or "bm_menu_dlc_locked" + end + + new_data.bitmap_texture = guis_catalog .. "textures/pd2/blackmarket/icons/melee_weapons/" .. tostring(new_data.name) + + if melee_weapon_id == "weapon" then + new_data.extra_bitmaps = {} + new_data.extra_bitmaps_shape = {} + local primary = managers.blackmarket:equipped_primary() + local primary_id = primary.weapon_id + guis_catalog = "guis/" + local bundle_folder = tweak_data.weapon[primary_id] and tweak_data.weapon[primary_id].texture_bundle_folder + if bundle_folder then + guis_catalog = guis_catalog .. "dlcs/" .. tostring(bundle_folder) .. "/" + end + table.insert(new_data.extra_bitmaps, guis_catalog .. "textures/pd2/blackmarket/icons/weapons/" .. tostring(primary_id)) + table.insert(new_data.extra_bitmaps_shape, { + x = 0, + y = -0.1, + w = 0.75, + h = 0.75 + }) + local secondary = managers.blackmarket:equipped_secondary() + local secondary_id = secondary.weapon_id + guis_catalog = "guis/" + local bundle_folder = tweak_data.weapon[secondary_id] and tweak_data.weapon[secondary_id].texture_bundle_folder + if bundle_folder then + guis_catalog = guis_catalog .. "dlcs/" .. tostring(bundle_folder) .. "/" + end + table.insert(new_data.extra_bitmaps, guis_catalog .. "textures/pd2/blackmarket/icons/weapons/" .. tostring(secondary_id)) + table.insert(new_data.extra_bitmaps_shape, { + x = 0, + y = 0.1, + w = 0.75, + h = 0.75 + }) + end + + if managers.blackmarket:got_new_drop("normal", "melee_weapons", melee_weapon_id) then + new_data.mini_icons = new_data.mini_icons or {} + table.insert(new_data.mini_icons, { + name = "new_drop", + texture = "guis/textures/pd2/blackmarket/inv_newdrop", + right = 0, + top = 0, + layer = 1, + w = 16, + h = 16, + stream = false + }) + new_data.new_drop_data = { + "normal", + "melee_weapons", + melee_weapon_id + } + end + + if new_data.unlocked then + new_data.comparision_data = managers.blackmarket:get_melee_weapon_stats(melee_weapon_id) + end + if new_data.unlocked and not new_data.equipped then + table.insert(new_data, "lo_mw_equip") + end + if new_data.unlocked and data.allow_preview and m_tweak_data.unit then + table.insert(new_data, "lo_mw_preview") + end + + Hooks:Call("BlackMarketGUIOnPopulateMeleeWeaponActionList", self, new_data) + + data[i] = new_data + index = i + + end + + for i = 1, max_items do + if not data[i] then + new_data = {} + new_data.name = "empty" + new_data.name_localized = "" + new_data.category = "melee_weapons" + new_data.slot = i + new_data.unlocked = true + new_data.equipped = false + data[i] = new_data + end + end + +end + +Hooks:RegisterHook("BlackMarketGUIOnPopulateMasks") +Hooks:RegisterHook("BlackMarketGUIOnPopulateMasksActionList") +function BlackMarketGui.populate_masks(self, data) + + local success, err = pcall(function() + + Hooks:Call("BlackMarketGUIOnPopulateMasks", self, data) + + local NOT_WIN_32 = SystemInfo:platform() ~= Idstring("WIN32") + local GRID_H_MUL = (NOT_WIN_32 and 7 or 6.6) / 8 + + local new_data = {} + local crafted_category = managers.blackmarket:get_crafted_category("masks") or {} + local mini_icon_helper = math.round((self._panel:h() - (tweak_data.menu.pd2_medium_font_size + 10) - 60) * GRID_H_MUL / 3) - 16 + local max_items = data.override_slots and data.override_slots[1] * data.override_slots[2] or 9 + local start_crafted_item = data.start_crafted_item or 1 + local hold_crafted_item = managers.blackmarket:get_hold_crafted_item() + local currently_holding = hold_crafted_item and hold_crafted_item.category == "masks" + local max_rows = tweak_data.gui.MAX_MASK_ROWS + max_items = max_rows * (data.override_slots and data.override_slots[2] or 3) + for i = 1, max_items do + data[i] = nil + end + + local guis_catalog = "guis/" + local index = 0 + for i, crafted in pairs(crafted_category) do + + index = i - start_crafted_item + 1 + guis_catalog = "guis/" + local bundle_folder = tweak_data.blackmarket.masks[crafted.mask_id] and tweak_data.blackmarket.masks[crafted.mask_id].texture_bundle_folder + if bundle_folder then + guis_catalog = guis_catalog .. "dlcs/" .. tostring(bundle_folder) .. "/" + end + + new_data = {} + new_data.name = crafted.mask_id + new_data.name_localized = managers.blackmarket:get_mask_name_by_category_slot("masks", i) + new_data.raw_name_localized = managers.localization:text(tweak_data.blackmarket.masks[new_data.name].name_id) + new_data.custom_name_text = managers.blackmarket:get_crafted_custom_name("masks", i, true) + new_data.custom_name_text_right = crafted.modded and -55 or -20 + new_data.custom_name_text_width = crafted.modded and 0.6 + new_data.category = "masks" + new_data.global_value = crafted.global_value + new_data.slot = i + new_data.unlocked = true + new_data.equipped = crafted.equipped + new_data.bitmap_texture = guis_catalog .. "textures/pd2/blackmarket/icons/masks/" .. new_data.name + new_data.stream = false + new_data.holding = currently_holding and hold_crafted_item.slot == i + local is_locked = tweak_data.lootdrop.global_values[new_data.global_value] and tweak_data.lootdrop.global_values[new_data.global_value].dlc and not managers.dlc:has_dlc(new_data.global_value) + local locked_parts = {} + if not is_locked then + for part, type in pairs(crafted.blueprint) do + if tweak_data.lootdrop.global_values[part.global_value] and tweak_data.lootdrop.global_values[part.global_value].dlc and not tweak_data.dlc[part.global_value].free and not managers.dlc:has_dlc(part.global_value) then + locked_parts[type] = part.global_value + is_locked = true + end + end + end + + if is_locked then + new_data.unlocked = false + new_data.lock_texture = self:get_lock_icon(new_data, "guis/textures/pd2/lock_incompatible") + new_data.dlc_locked = tweak_data.lootdrop.global_values[new_data.global_value].unlock_id or "bm_menu_dlc_locked" + end + + if currently_holding then + if i ~= 1 then + new_data.selected_text = managers.localization:to_upper_text("bm_menu_btn_swap_mask") + end + + if i ~= 1 and new_data.slot ~= hold_crafted_item.slot then + table.insert(new_data, "m_swap") + end + + table.insert(new_data, "i_stop_move") + else + if new_data.unlocked then + if not new_data.equipped then + table.insert(new_data, "m_equip") + end + + if i ~= 1 and new_data.equipped then + table.insert(new_data, "m_move") + end + + if not crafted.modded and managers.blackmarket:can_modify_mask(i) and i ~= 1 then + table.insert(new_data, "m_mod") + end + + if i ~= 1 then + table.insert(new_data, "m_preview") + end + + end + + if i ~= 1 then + Hooks:Call("BlackMarketGUIOnPopulateMasksActionList", self, new_data) + end + + if i ~= 1 then + if 0 < managers.money:get_mask_sell_value(new_data.name, new_data.global_value) then + table.insert(new_data, "m_sell") + else + table.insert(new_data, "m_remove") + end + + end + + end + + if crafted.modded then + new_data.mini_icons = {} + local color_1 = tweak_data.blackmarket.colors[crafted.blueprint.color.id].colors[1] + local color_2 = tweak_data.blackmarket.colors[crafted.blueprint.color.id].colors[2] + table.insert(new_data.mini_icons, { + texture = false, + w = 16, + h = 16, + right = 0, + bottom = 0, + layer = 1, + color = color_2 + }) + table.insert(new_data.mini_icons, { + texture = false, + w = 16, + h = 16, + right = 18, + bottom = 0, + layer = 1, + color = color_1 + }) + if locked_parts.color then + local texture = self:get_lock_icon({ + global_value = locked_parts.color + }) + table.insert(new_data.mini_icons, { + texture = texture, + w = 32, + h = 32, + right = 2, + bottom = -5, + layer = 2, + color = tweak_data.screen_colors.important_1 + }) + end + + local pattern = crafted.blueprint.pattern.id + if pattern == "solidfirst" or pattern == "solidsecond" then + else + local material_id = crafted.blueprint.material.id + guis_catalog = "guis/" + local bundle_folder = tweak_data.blackmarket.materials[material_id] and tweak_data.blackmarket.materials[material_id].texture_bundle_folder + if bundle_folder then + guis_catalog = guis_catalog .. "dlcs/" .. tostring(bundle_folder) .. "/" + end + + local right = -3 + local bottom = 38 - (NOT_WIN_32 and 20 or 10) + local w = 42 + local h = 42 + table.insert(new_data.mini_icons, { + texture = guis_catalog .. "textures/pd2/blackmarket/icons/materials/" .. material_id, + right = right, + bottom = bottom, + w = w, + h = h, + layer = 1, + stream = true + }) + if locked_parts.material then + local texture = self:get_lock_icon({ + global_value = locked_parts.material + }) + table.insert(new_data.mini_icons, { + texture = texture, + w = 32, + h = 32, + right = right + (w - 32) / 2, + bottom = bottom + (h - 32) / 2, + layer = 2, + color = tweak_data.screen_colors.important_1 + }) + end + + end + + do + local right = -3 + local bottom = math.round(mini_icon_helper - 6 - 6 - 42) + local w = 42 + local h = 42 + table.insert(new_data.mini_icons, { + texture = tweak_data.blackmarket.textures[pattern].texture, + right = right, + bottom = bottom, + w = h, + h = w, + layer = 1, + stream = true, + render_template = Idstring("VertexColorTexturedPatterns") + }) + if locked_parts.pattern then + local texture = self:get_lock_icon({ + global_value = locked_parts.pattern + }) + table.insert(new_data.mini_icons, { + texture = texture, + w = 32, + h = 32, + right = right + (w - 32) / 2, + bottom = bottom + (h - 32) / 2, + layer = 2, + color = tweak_data.screen_colors.important_1 + }) + end + + end + + new_data.mini_icons.borders = true + elseif i ~= 1 and managers.blackmarket:can_modify_mask(i) and managers.blackmarket:got_new_drop("normal", "mask_mods", crafted.mask_id) then + new_data.mini_icons = new_data.mini_icons or {} + table.insert(new_data.mini_icons, { + name = "new_drop", + texture = "guis/textures/pd2/blackmarket/inv_newdrop", + right = 0, + top = 0, + layer = 1, + w = 16, + h = 16, + stream = false, + visible = true + }) + new_data.new_drop_data = {} + end + + data[index] = new_data + + end + + local can_buy_masks = true + for i = 1, max_items do + if not data[i] then + index = i + start_crafted_item - 1 + can_buy_masks = managers.blackmarket:is_mask_slot_unlocked(i) + new_data = {} + if can_buy_masks then + new_data.name = "bm_menu_btn_buy_new_mask" + new_data.name_localized = managers.localization:text("bm_menu_empty_mask_slot") + new_data.mid_text = {} + new_data.mid_text.noselected_text = new_data.name_localized + new_data.mid_text.noselected_color = tweak_data.screen_colors.button_stage_3 + if not currently_holding or not new_data.mid_text.noselected_text then + end + + new_data.mid_text.selected_text = managers.localization:text("bm_menu_btn_buy_new_mask") + new_data.mid_text.selected_color = currently_holding and new_data.mid_text.noselected_color or tweak_data.screen_colors.button_stage_2 + new_data.empty_slot = true + new_data.category = "masks" + new_data.slot = index + new_data.unlocked = true + new_data.equipped = false + new_data.num_backs = 0 + new_data.cannot_buy = not can_buy_masks + if currently_holding then + if i ~= 1 then + new_data.selected_text = managers.localization:to_upper_text("bm_menu_btn_place_mask") + end + + if i ~= 1 then + table.insert(new_data, "m_place") + end + + table.insert(new_data, "i_stop_move") + else + table.insert(new_data, "em_buy") + end + + if index ~= 1 and managers.blackmarket:got_new_drop(nil, "mask_buy", nil) then + new_data.mini_icons = new_data.mini_icons or {} + table.insert(new_data.mini_icons, { + name = "new_drop", + texture = "guis/textures/pd2/blackmarket/inv_newdrop", + right = 0, + top = 0, + layer = 1, + w = 16, + h = 16, + stream = false, + visible = false + }) + new_data.new_drop_data = {} + end + + else + new_data.name = "bm_menu_btn_buy_mask_slot" + new_data.name_localized = managers.localization:text("bm_menu_locked_mask_slot") + new_data.empty_slot = true + new_data.category = "masks" + new_data.slot = index + new_data.unlocked = true + new_data.equipped = false + new_data.num_backs = 0 + new_data.lock_texture = "guis/textures/pd2/blackmarket/money_lock" + new_data.lock_color = tweak_data.screen_colors.button_stage_3 + new_data.lock_shape = { + w = 32, + h = 32, + x = 0, + y = -32 + } + new_data.locked_slot = true + new_data.dlc_locked = managers.experience:cash_string(managers.money:get_buy_mask_slot_price()) + new_data.mid_text = {} + new_data.mid_text.noselected_text = new_data.name_localized + new_data.mid_text.noselected_color = tweak_data.screen_colors.button_stage_3 + new_data.mid_text.is_lock_same_color = true + if currently_holding then + new_data.mid_text.selected_text = new_data.mid_text.noselected_text + new_data.mid_text.selected_color = new_data.mid_text.noselected_color + table.insert(new_data, "i_stop_move") + elseif managers.money:can_afford_buy_mask_slot() then + new_data.mid_text.selected_text = managers.localization:text("bm_menu_btn_buy_mask_slot") + new_data.mid_text.selected_color = tweak_data.screen_colors.button_stage_2 + table.insert(new_data, "em_unlock") + else + new_data.mid_text.selected_text = managers.localization:text("bm_menu_cannot_buy_mask_slot") + new_data.mid_text.selected_color = tweak_data.screen_colors.important_1 + new_data.dlc_locked = new_data.dlc_locked .. " " .. managers.localization:to_upper_text("bm_menu_cannot_buy_mask_slot") + new_data.mid_text.lock_noselected_color = tweak_data.screen_colors.important_1 + new_data.cannot_buy = true + end + + end + + data[i] = new_data + end + + end + + end) + + if not success then Print(err) end + +end + +Hooks:RegisterHook("BlackMarketGUIOnPopulateMods") +Hooks:RegisterHook("BlackMarketGUIOnPopulateModsActionList") +function BlackMarketGui.populate_mods(self, data) + + local success, err = pcall(function() + + Hooks:Call("BlackMarketGUIOnPopulateMods", self, data) + + local new_data = {} + local default_mod = data.on_create_data.default_mod + local global_values = managers.blackmarket:get_crafted_category(data.prev_node_data.category)[data.prev_node_data.slot].global_values or {} + local gvs = {} + local mod_t = {} + local num_steps = #data.on_create_data + local achievement_tracker = tweak_data.achievement.weapon_part_tracker + local guis_catalog = "guis/" + + for index, mod_t in ipairs(data.on_create_data) do + + local mod_name = mod_t[1] + local mod_default = mod_t[2] + local mod_global_value = mod_t[3] or "normal" + guis_catalog = "guis/" + local bundle_folder = tweak_data.blackmarket.weapon_mods[mod_name] and tweak_data.blackmarket.weapon_mods[mod_name].texture_bundle_folder + if bundle_folder then + guis_catalog = guis_catalog .. "dlcs/" .. tostring(bundle_folder) .. "/" + end + + new_data = {} + new_data.name = mod_name or data.prev_node_data.name + if not mod_name or not managers.weapon_factory:get_part_name_by_part_id(mod_name) then + end + + new_data.name_localized = managers.weapon_factory:get_part_name_by_part_id(mod_name) + new_data.category = data.prev_node_data.category + new_data.bitmap_texture = guis_catalog .. "textures/pd2/blackmarket/icons/mods/" .. new_data.name + new_data.slot = not data.slot and data.prev_node_data and data.prev_node_data.slot + new_data.global_value = mod_global_value + new_data.unlocked = mod_default or managers.blackmarket:get_item_amount(new_data.global_value, "weapon_mods", new_data.name, true) + new_data.equipped = false + new_data.stream = true + new_data.default_mod = default_mod + new_data.is_internal = tweak_data.weapon.factory:is_part_internal(new_data.name) + new_data.free_of_charge = tweak_data.blackmarket.weapon_mods[mod_name] and tweak_data.blackmarket.weapon_mods[mod_name].is_a_unlockable + new_data.unlock_tracker = achievement_tracker[new_data.name] or false + if tweak_data.lootdrop.global_values[mod_global_value] and tweak_data.lootdrop.global_values[mod_global_value].dlc and not tweak_data.dlc[mod_global_value].free and not managers.dlc:has_dlc(mod_global_value) then + if type(new_data.unlocked) == "number" then + new_data.unlocked = -math.abs(new_data.unlocked) + end + new_data.unlocked = new_data.unlocked ~= 0 and new_data.unlocked or false + new_data.lock_texture = self:get_lock_icon(new_data) + new_data.dlc_locked = tweak_data.lootdrop.global_values[new_data.global_value].unlock_id or "bm_menu_dlc_locked" + end + + local weapon_id = managers.blackmarket:get_crafted_category(new_data.category)[new_data.slot].weapon_id + new_data.price = managers.money:get_weapon_modify_price(weapon_id, new_data.name, new_data.global_value) + new_data.can_afford = managers.money:can_afford_weapon_modification(weapon_id, new_data.name, new_data.global_value) + local font, font_size + local no_upper = false + if not new_data.lock_texture and (not new_data.unlocked or new_data.unlocked == 0) then + local selected_text, noselected_text + if not new_data.dlc_locked and new_data.unlock_tracker then + local text_id = "bm_menu_no_items" + local progress = "" + local stat = new_data.unlock_tracker.stat or false + local max_progress = new_data.unlock_tracker.max_progress or 0 + local award = new_data.unlock_tracker.award or false + if new_data.unlock_tracker.text_id then + if max_progress > 0 and stat then + local progress_left = max_progress - (managers.achievment:get_stat(stat) or 0) + if progress_left > 0 then + progress = tostring(progress_left) + text_id = new_data.unlock_tracker.text_id + font = small_font + font_size = small_font_size + no_upper = true + end + + elseif award then + local achievement = managers.achievment:get_info(award) + text_id = new_data.unlock_tracker.text_id + font = small_font + font_size = small_font_size + no_upper = true + end + + selected_text = managers.localization:text(text_id, {progress = progress}) + end + + end + + selected_text = selected_text or managers.localization:text("bm_menu_no_items") + noselected_text = selected_text + new_data.mid_text = {} + new_data.mid_text.selected_text = selected_text + new_data.mid_text.selected_color = tweak_data.screen_colors.text + new_data.mid_text.noselected_text = noselected_text + new_data.mid_text.noselected_color = tweak_data.screen_colors.text + new_data.mid_text.vertical = "center" + new_data.mid_text.font = font + new_data.mid_text.font_size = font_size + new_data.mid_text.no_upper = no_upper + new_data.lock_texture = true + end + + if mod_name then + local forbid = managers.blackmarket:can_modify_weapon(new_data.category, new_data.slot, new_data.name) + if forbid then + if type(new_data.unlocked) == "number" then + new_data.unlocked = -math.abs(new_data.unlocked) + else + new_data.unlocked = false + end + + new_data.lock_texture = self:get_lock_icon(new_data, "guis/textures/pd2/lock_incompatible") + new_data.mid_text = nil + new_data.conflict = managers.localization:text("bm_menu_" .. tostring(tweak_data.weapon.factory.parts[forbid].type)) + end + + local weapon = managers.blackmarket:get_crafted_category_slot(data.prev_node_data.category, data.prev_node_data.slot) or {} + local gadget + local mod_type = tweak_data.weapon.factory.parts[new_data.name].type + local sub_type = tweak_data.weapon.factory.parts[new_data.name].sub_type + local is_auto = weapon and tweak_data.weapon[weapon.weapon_id] and tweak_data.weapon[weapon.weapon_id].FIRE_MODE == "auto" + if mod_type == "gadget" then + gadget = sub_type + end + + local silencer = sub_type == "silencer" and true + local texture = managers.menu_component:get_texture_from_mod_type(mod_type, sub_type, gadget, silencer, is_auto) + new_data.desc_mini_icons = {} + if DB:has(Idstring("texture"), texture) then + table.insert(new_data.desc_mini_icons, { + texture = texture, + right = 0, + bottom = 0, + w = 16, + h = 16 + }) + end + + local is_gadget = false + if not new_data.conflict and new_data.unlocked and not is_gadget and not new_data.dlc_locked then + new_data.comparision_data = managers.blackmarket:get_weapon_stats_with_mod(new_data.category, new_data.slot, mod_name) + end + + if managers.blackmarket:got_new_drop(mod_global_value, "weapon_mods", mod_name) then + new_data.mini_icons = new_data.mini_icons or {} + table.insert(new_data.mini_icons, { + name = "new_drop", + texture = "guis/textures/pd2/blackmarket/inv_newdrop", + right = 0, + top = 0, + layer = 1, + w = 16, + h = 16, + stream = false + }) + new_data.new_drop_data = { + new_data.global_value or "normal", + "weapon_mods", + mod_name + } + end + + end + + if mod_name and new_data.unlocked then + if type(new_data.unlocked) ~= "number" or new_data.unlocked > 0 then + if new_data.can_afford then + table.insert(new_data, "wm_buy") + end + + table.insert(new_data, "wm_preview") + if not new_data.is_internal then + table.insert(new_data, "wm_preview_mod") + end + + else + table.insert(new_data, "wm_preview") + end + + end + + Hooks:Call("BlackMarketGUIOnPopulateModsActionList", self, new_data) + + data[index] = new_data + + end + + for i = 1, math.max(math.ceil(num_steps / 3), 3) * 3 do + if not data[i] then + new_data = {} + new_data.name = "empty" + new_data.name_localized = "" + new_data.category = data.category + new_data.slot = i + new_data.unlocked = true + new_data.equipped = false + data[i] = new_data + end + + end + + local weapon_blueprint = managers.blackmarket:get_weapon_blueprint(data.prev_node_data.category, data.prev_node_data.slot) or {} + local equipped + for i, mod in ipairs(data) do + for k, weapon_mod in ipairs(weapon_blueprint) do + + if mod.name == weapon_mod and (not global_values[weapon_mod] or global_values[weapon_mod] == data[i].global_value) then + equipped = i + end + + end + end + + if equipped then + + data[equipped].equipped = true + data[equipped].unlocked = data[equipped].unlocked or true + data[equipped].mid_text = nil + data[equipped].lock_texture = nil + + for i = 1, #data[equipped] do + table.remove(data[equipped], 1) + end + + data[equipped].price = 0 + data[equipped].can_afford = true + + table.insert(data[equipped], "wm_remove_buy") + + if not data[equipped].is_internal then + table.insert(data[equipped], "wm_remove_preview_mod") + table.insert(data[equipped], "wm_remove_preview") + else + table.insert(data[equipped], "wm_preview") + end + + Hooks:Call("BlackMarketGUIOnPopulateModsActionList", self, data[equipped]) + + local factory = tweak_data.weapon.factory.parts[data[equipped].name] + if data.name == "sight" and factory and factory.texture_switch then + + table.insert(data[equipped], "wm_reticle_switch_menu") + local reticle_texture = managers.blackmarket:get_part_texture_switch(data[equipped].category, data[equipped].slot, data[equipped].name) + if reticle_texture and reticle_texture ~= "" then + data[equipped].mini_icons = data[equipped].mini_icons or {} + table.insert(data[equipped].mini_icons, { + texture = reticle_texture, + right = 1, + bottom = 1, + layer = 2, + w = 30, + h = 30, + stream = true, + blend_mode = "add" + }) + table.insert(data[equipped].mini_icons, { + color = Color.black, + right = -5, + bottom = -5, + layer = 0, + alpha = 0.4, + w = 42, + h = 42, + borders = true + }) + end + + end + + if not data[equipped].conflict then + + if data[equipped].default_mod then + data[equipped].comparision_data = managers.blackmarket:get_weapon_stats_with_mod(data[equipped].category, data[equipped].slot, data[equipped].default_mod) + else + data[equipped].comparision_data = managers.blackmarket:get_weapon_stats_without_mod(data[equipped].category, data[equipped].slot, data[equipped].name) + end + + end + + end + + end) + if not success then Print("[Error] " .. err) end + +end + +Hooks:RegisterHook("BlackMarketGUIOnPopulateMaskMods") +Hooks:RegisterHook("BlackMarketGUIOnPopulateMaskModsActionList") +function BlackMarketGui.populate_choose_mask_mod(self, data) + + local success, err = pcall(function() + + Hooks:Call("BlackMarketGUIOnPopulateMaskMods", self, data) + + local new_data = {} + local index = 1 + local equipped_mod = managers.blackmarket:customize_mask_category_id(data.category) + local guis_catalog = "guis/" + + for k, mods in pairs(data.on_create_data) do + + guis_catalog = "guis/" + local bundle_folder = tweak_data.blackmarket[data.category][mods.id] and tweak_data.blackmarket[data.category][mods.id].texture_bundle_folder + if bundle_folder then + guis_catalog = guis_catalog .. "dlcs/" .. tostring(bundle_folder) .. "/" + end + + new_data = {} + new_data.name = mods.id + new_data.name_localized = managers.localization:text(tweak_data.blackmarket[data.category][new_data.name].name_id) + new_data.category = data.category + new_data.slot = index + new_data.prev_slot = data.prev_node_data and data.prev_node_data.slot + new_data.unlocked = mods.default or mods.amount + new_data.amount = mods.amount or 0 + new_data.equipped = equipped_mod == mods.id + new_data.equipped_text = managers.localization:text("bm_menu_chosen") + new_data.mods = mods + new_data.stream = data.category ~= "colors" + new_data.global_value = mods.global_value + local is_locked = false + if new_data.amount < 1 and mods.id ~= "plastic" and mods.id ~= "no_color_full_material" and not mods.free_of_charge then + if type(new_data.unlocked) == "number" then + new_data.unlocked = -math.abs(new_data.unlocked) + end + new_data.lock_texture = true + new_data.dlc_locked = "bm_menu_amount_locked" + is_locked = true + end + if tweak_data.lootdrop.global_values[new_data.global_value] and tweak_data.lootdrop.global_values[new_data.global_value].dlc and not tweak_data.dlc[new_data.global_value].free and not managers.dlc:has_dlc(new_data.global_value) then + if type(new_data.unlocked) == "number" then + new_data.unlocked = -math.abs(new_data.unlocked) + end + new_data.lock_texture = self:get_lock_icon(new_data) + new_data.dlc_locked = tweak_data.lootdrop.global_values[new_data.global_value].unlock_id or "bm_menu_dlc_locked" + is_locked = true + end + + if data.category == "colors" then + new_data.bitmap_texture = "guis/textures/pd2/blackmarket/icons/colors/color_bg" + new_data.extra_bitmaps = {} + table.insert(new_data.extra_bitmaps, "guis/textures/pd2/blackmarket/icons/colors/color_02") + table.insert(new_data.extra_bitmaps, "guis/textures/pd2/blackmarket/icons/colors/color_01") + new_data.extra_bitmaps_colors = {} + table.insert(new_data.extra_bitmaps_colors, tweak_data.blackmarket.colors[new_data.name].colors[2]) + table.insert(new_data.extra_bitmaps_colors, tweak_data.blackmarket.colors[new_data.name].colors[1]) + elseif data.category == "textures" then + new_data.bitmap_texture = tweak_data.blackmarket[data.category][mods.id].texture + new_data.render_template = Idstring("VertexColorTexturedPatterns") + else + new_data.bitmap_texture = guis_catalog .. "textures/pd2/blackmarket/icons/" .. tostring(data.category) .. "/" .. new_data.name + if mods.bitmap_texture_override then + new_data.bitmap_texture = guis_catalog .. "textures/pd2/blackmarket/icons/" .. tostring(data.category) .. "/" .. mods.bitmap_texture_override + end + end + + if managers.blackmarket:got_new_drop(new_data.global_value or "normal", new_data.category, new_data.name) then + new_data.mini_icons = new_data.mini_icons or {} + table.insert(new_data.mini_icons, { + name = "new_drop", + texture = "guis/textures/pd2/blackmarket/inv_newdrop", + right = 0, + top = 0, + layer = 1, + w = 16, + h = 16, + stream = false + }) + new_data.new_drop_data = { + new_data.global_value or "normal", + new_data.category, + new_data.name + } + end + + new_data.btn_text_params = { + type = managers.localization:text("bm_menu_" .. data.category) + } + if not is_locked then + + table.insert(new_data, "mp_choose") + table.insert(new_data, "mp_preview") + + end + + if managers.blackmarket:can_finish_customize_mask() and managers.blackmarket:can_afford_customize_mask() then + table.insert(new_data, "mm_buy") + end + + Hooks:Call("BlackMarketGUIOnPopulateMaskModsActionList", self, new_data) + + data[index] = new_data + index = index + 1 + + end + + if #data == 0 then + new_data = {} + new_data.name = "bm_menu_nothing" + new_data.empty_slot = true + new_data.category = data.category + new_data.slot = 1 + new_data.unlocked = true + new_data.can_afford = true + new_data.equipped = false + data[1] = new_data + end + + local max_mask_mods = #data.on_create_data + for i = 1, math.ceil(max_mask_mods / data.override_slots[1]) * data.override_slots[1] do + if not data[i] then + new_data = {} + new_data.name = "empty" + new_data.name_localized = "" + new_data.category = data.category + new_data.slot = i + new_data.unlocked = true + new_data.equipped = false + data[i] = new_data + end + + end + + end) + if not success then Print("[Error] " .. err) end + +end + + +Hooks:RegisterHook("BlackMarketGUIStartPageData") +function BlackMarketGui._start_page_data(self) + + local data = {} + table.insert(data, { + name = "bm_menu_primaries", + category = "primaries", + on_create_func_name = "populate_primaries", + identifier = self.identifiers.weapon, + override_slots = {3, 3} + }) + table.insert(data, { + name = "bm_menu_secondaries", + category = "secondaries", + on_create_func_name = "populate_secondaries", + identifier = self.identifiers.weapon, + override_slots = {3, 3} + }) + table.insert(data, { + name = "bm_menu_melee_weapons", + category = "melee_weapons", + on_create_func_name = "populate_melee_weapons", + allow_preview = true, + override_slots = {3, 3}, + identifier = Idstring("melee_weapon") + }) + table.insert(data, { + name = "bm_menu_armors", + category = "armors", + on_create_func_name = "populate_armors", + override_slots = {4, 2}, + identifier = self.identifiers.armor + }) + table.insert(data, { + name = "bm_menu_deployables", + category = "deployables", + on_create_func_name = "populate_deployables", + override_slots = {4, 2}, + identifier = Idstring("deployable") + }) + table.insert(data, { + name = "bm_menu_masks", + category = "masks", + on_create_func_name = "populate_masks", + identifier = self.identifiers.mask, + override_slots = {3, 3}, + start_crafted_item = 1 + }) + if not managers.network:session() then + table.insert(data, { + name = "bm_menu_characters", + category = "characters", + on_create_func_name = "populate_characters", + override_slots = {4, 2}, + identifier = self.identifiers.character + }) + end + + Hooks:Call("BlackMarketGUIStartPageData", self, data) + + data.topic_id = "menu_inventory" + self:_cleanup_blackmarket() + return data + +end + +Hooks:RegisterHook("BlackMarketGUIUpdateInfoText") +function BlackMarketGui.update_info_text(self) + self.orig.update_info_text(self) + Hooks:Call("BlackMarketGUIUpdateInfoText", self) +end + +function BlackMarketGui._update_info_text(self, slot_data, updated_texts, data, scale_override) + + local ignore_lock = false + local is_renaming_this = false + if data ~= nil then + ignore_lock = data.ignore_lock or ignore_lock + is_renaming_this = data.is_renaming_this or is_renaming_this + end + + if self._desc_mini_icons then + for _, gui_object in pairs(self._desc_mini_icons) do + self._panel:remove(gui_object[1]) + end + end + self._desc_mini_icons = {} + local desc_mini_icons = self._slot_data.desc_mini_icons + local info_box_panel = self._panel:child("info_box_panel") + if desc_mini_icons and 0 < table.size(desc_mini_icons) then + for _, mini_icon in pairs(desc_mini_icons) do + local new_icon = self._panel:bitmap({ + texture = mini_icon.texture, + x = info_box_panel:left() + 10 + mini_icon.right, + w = mini_icon.w or 32, + h = mini_icon.h or 32 + }) + table.insert(self._desc_mini_icons, {new_icon, 1}) + end + updated_texts[2].text = string.rep(" ", table.size(desc_mini_icons)) .. updated_texts[2].text + else + end + if not ignore_lock and slot_data.lock_texture and slot_data.lock_texture ~= true then + local new_icon = self._panel:bitmap({ + texture = slot_data.lock_texture, + x = info_box_panel:left() + 10, + w = 20, + h = 20, + color = self._info_texts[3]:color(), + blend_mode = "add" + }) + updated_texts[3].text = " " .. updated_texts[3].text + table.insert(self._desc_mini_icons, {new_icon, 2}) + else + end + if is_renaming_this and self._rename_info_text then + local text = self._renaming_item.custom_name ~= "" and self._renaming_item.custom_name or "##" .. tostring(slot_data.raw_name_localized) .. "##" + updated_texts[self._rename_info_text].text = text + updated_texts[self._rename_info_text].resource_color = tweak_data.screen_colors.text:with_alpha(0.35) + end + for id, _ in ipairs(self._info_texts) do + self:set_info_text(id, updated_texts[id].text, updated_texts[id].resource_color) + end + local _, _, _, th = self._info_texts[1]:text_rect() + self._info_texts[1]:set_h(th) + local y = self._info_texts[1]:bottom() + local title_offset = y + local bg = self._info_texts_bg[1] + if alive(bg) then + bg:set_shape(self._info_texts[1]:shape()) + end + local below_y + for i = 2, #self._info_texts do + local info_text = self._info_texts[i] + info_text:set_font_size(small_font_size) + _, _, _, th = info_text:text_rect() + info_text:set_y(y) + info_text:set_h(th) + if updated_texts[i].below_stats then + if slot_data.comparision_data and alive(self._stats_text_modslist) then + info_text:set_world_y(below_y or self._stats_text_modslist:world_top()) + below_y = (below_y or info_text:world_y()) + th + else + info_text:set_top((below_y or info_text:top()) + 20) + below_y = (below_y or info_text:top()) + th + end + end + local scale = 1 + if info_text:bottom() > self._info_texts_panel:h() then + scale = self._info_texts_panel:h() / info_text:bottom() + end + + if scale_override then + if type(scale_override) == "number" then + scale = scale_override + end + if type(scale_override) == "table" then + scale = scale_override[i] + end + end + + info_text:set_font_size(small_font_size * scale) + _, _, _, th = info_text:text_rect() + info_text:set_h(th) + local bg = self._info_texts_bg[i] + if alive(bg) then + bg:set_shape(info_text:shape()) + end + y = info_text:bottom() + end + for _, desc_mini_icon in ipairs(self._desc_mini_icons) do + desc_mini_icon[1]:set_y(title_offset) + desc_mini_icon[1]:set_world_top(self._info_texts[desc_mini_icon[2]]:world_bottom() + (2 - (desc_mini_icon[2] - 1) * 3)) + end + if is_renaming_this and self._rename_info_text and self._rename_caret then + local info_text = self._info_texts[self._rename_info_text] + local x, y, w, h = info_text:text_rect() + if self._renaming_item.custom_name == "" then + w = 0 + end + self._rename_caret:set_w(2) + self._rename_caret:set_h(h) + self._rename_caret:set_world_position(x + w, y) + end + +end + +Hooks:RegisterHook("BlackMarketGUIOnPopulateBuyMasks") +Hooks:RegisterHook("BlackMarketGUIOnPopulateBuyMasksActionList") +function BlackMarketGui.populate_buy_mask(self, data) + + Hooks:Call("BlackMarketGUIOnPopulateBuyMasks", self, data) + + local new_data = {} + local guis_catalog = "guis/" + local max_masks = #data.on_create_data + + for i = 1, max_masks do + data[i] = nil + end + + for i = 1, #data.on_create_data do + + guis_catalog = "guis/" + + local bundle_folder = tweak_data.blackmarket.masks[data.on_create_data[i].mask_id] and tweak_data.blackmarket.masks[data.on_create_data[i].mask_id].texture_bundle_folder + if bundle_folder then + guis_catalog = guis_catalog .. "dlcs/" .. tostring(bundle_folder) .. "/" + end + + new_data = {} + new_data.name = data.on_create_data[i].mask_id + new_data.name_localized = managers.localization:text(tweak_data.blackmarket.masks[new_data.name].name_id) + new_data.category = data.category + new_data.slot = data.prev_node_data and data.prev_node_data.slot + new_data.global_value = data.on_create_data[i].global_value + new_data.unlocked = managers.blackmarket:get_item_amount(new_data.global_value, "masks", new_data.name, true) or 0 + new_data.equipped = false + new_data.num_backs = data.prev_node_data.num_backs + 1 + new_data.bitmap_texture = guis_catalog .. "textures/pd2/blackmarket/icons/masks/" .. new_data.name + new_data.stream = false + + if not new_data.global_value then + Application:debug("BlackMarketGui:populate_buy_mask( data ) Missing global value on mask", new_data.name) + end + + if tweak_data.lootdrop.global_values[new_data.global_value] and tweak_data.lootdrop.global_values[new_data.global_value].dlc and not tweak_data.dlc[new_data.global_value].free and not managers.dlc:has_dlc(new_data.global_value) then + if type(new_data.unlocked) == "number" then + new_data.unlocked = -math.abs(new_data.unlocked) + end + new_data.lock_texture = self:get_lock_icon(new_data) + new_data.dlc_locked = tweak_data.lootdrop.global_values[new_data.global_value].unlock_id or "bm_menu_dlc_locked" + end + + if tweak_data.blackmarket.masks[new_data.name].infamy_lock then + + local infamy_lock = tweak_data.blackmarket.masks[new_data.name].infamy_lock + local is_unlocked = managers.infamy:owned(infamy_lock) + if not is_unlocked then + if type(new_data.unlocked) == "number" then + new_data.unlocked = -math.abs(new_data.unlocked) + end + new_data.lock_texture = "guis/textures/pd2/lock_infamy" + new_data.infamy_lock = infamy_lock + end + + end + + if new_data.unlocked and new_data.unlocked > 0 then + + table.insert(new_data, "bm_buy") + table.insert(new_data, "bm_preview") + if 0 < managers.money:get_mask_sell_value(new_data.name, new_data.global_value) then + table.insert(new_data, "bm_sell") + end + + else + + new_data.mid_text = "" + new_data.lock_texture = new_data.lock_texture or true + + end + + Hooks:Call("BlackMarketGUIOnPopulateBuyMasksActionList", self, new_data) + + if managers.blackmarket:got_new_drop(new_data.global_value or "normal", "masks", new_data.name) then + + new_data.mini_icons = new_data.mini_icons or {} + table.insert(new_data.mini_icons, { + name = "new_drop", + texture = "guis/textures/pd2/blackmarket/inv_newdrop", + right = 0, + top = 0, + layer = 1, + w = 16, + h = 16, + stream = false + }) + new_data.new_drop_data = { + new_data.global_value or "normal", + "masks", + new_data.name + } + + end + + data[i] = new_data + + end + + local max_page = data.override_slots[1] * data.override_slots[2] + for i = 1, math.max(math.ceil(max_masks / data.override_slots[1]) * data.override_slots[1], max_page) do + + if not data[i] then + new_data = {} + new_data.name = "empty" + new_data.name_localized = "" + new_data.category = data.category + new_data.slot = i + new_data.unlocked = true + new_data.equipped = false + data[i] = new_data + end + + end + +end + +Hooks:RegisterHook("BlackMarketGUIChooseMaskPartCallback") +function BlackMarketGui.choose_mask_part_callback(self, data) + local r = Hooks:ReturnCall("BlackMarketGUIChooseMaskPartCallback", self, data) + if r then + return + end + return self.orig.choose_mask_part_callback(self, data) +end + +Hooks:RegisterHook("BlackMarketGUIMouseReleased") +function BlackMarketGui.mouse_released(self, o, button, x, y) + if not self._enabled then + return + end + self.orig.mouse_released(self, o, button, x, y) + Hooks:Call("BlackMarketGUIMouseReleased", self, o, button, x, y) +end + +Hooks:RegisterHook("BlackMarketGUIOnPreviewWeapon") +function BlackMarketGui._preview_weapon(self, data) + self.orig._preview_weapon(self, data) + Hooks:Call("BlackMarketGUIOnPreviewWeapon", self, data) +end diff --git a/GoonMod/lua/BlackMarketManager.lua b/GoonMod/lua/BlackMarketManager.lua new file mode 100644 index 0000000..20373ea --- /dev/null +++ b/GoonMod/lua/BlackMarketManager.lua @@ -0,0 +1,393 @@ + +CloneClass( BlackMarketManager ) + +local INV_TO_CRAFT = Idstring("inventory_to_crafted") +local CRAFT_TO_INV = Idstring("crafted_to_inventroy") +local INV_ADD = Idstring("add_to_inventory") +local INV_REMOVE = Idstring("remove_from_inventory") +local CRAFT_ADD = Idstring("add_to_crafted") +local CRAFT_REMOVE = Idstring("remove_from_crafted") + +Hooks:RegisterHook("BlackMarketManagerPostSetup") +function BlackMarketManager._setup(self) + self.orig._setup(self) + Hooks:Call("BlackMarketManagerPostSetup", self) +end + +Hooks:RegisterHook("BlackMarketManagerModifyGetInventoryCategory") +function BlackMarketManager.get_inventory_category(self, category) + + local t = {} + + for global_value, categories in pairs(self._global.inventory) do + if categories[category] then + + for id, amount in pairs(categories[category]) do + table.insert(t, { + id = id, + global_value = global_value, + amount = amount + }) + end + + end + end + + Hooks:Call("BlackMarketManagerModifyGetInventoryCategory", self, category, t) + + return t + +end + +function BlackMarketManager:get_mods_on_weapon(category, slot) + local _global = Global.blackmarket_manager + if not _global.crafted_items[category] or not _global.crafted_items[category][slot] then + return + end + return _global.crafted_items[category][slot].blueprint +end + +function BlackMarketManager:on_traded_weapon(category, slot, remove_mods, skip_verification) + + local _global = Global.blackmarket_manager + if not _global.crafted_items[category] or not _global.crafted_items[category][slot] then + return + end + + local global_values = _global.crafted_items[category][slot].global_values or {} + local blueprint = _global.crafted_items[category][slot].blueprint + local default_blueprint = managers.weapon_factory:get_default_blueprint_by_factory_id( _global.crafted_items[category][slot].factory_id ) + + for i, default_part in ipairs(default_blueprint) do + table.delete(blueprint, default_part) + end + + -- Remove mods if we traded them + if remove_mods then + + for k, part_id in pairs(blueprint) do + local global_value = global_values[part_id] or "normal" + self:add_to_inventory(global_value, "weapon_mods", part_id, true) + self:alter_global_value_item(global_value, category, slot, part_id, CRAFT_REMOVE) + end + + end + + _global.crafted_items[category][slot] = nil + if not skip_verification then + self:_verfify_equipped_category(category) + if category == "primaries" then + self:_update_menu_scene_primary() + elseif category == "secondaries" then + self:_update_menu_scene_secondary() + end + + end + +end + +function BlackMarketManager:on_traded_mod(part_id) + + -- Find mod using part id + for k, v in pairs( tweak_data.blackmarket["weapon_mods"] ) do + if k == part_id then + + -- Get global value and amount of mod + local global_value = v.global_value or v.dlc or "normal" + local mod_amt = self:get_item_amount(global_value, "weapon_mods", part_id, true) + + -- Check if we have the mod before trying to remove it + if mod_amt > 0 then + self:remove_item(global_value, "weapon_mods", part_id) + end + + end + end + +end + +function BlackMarketManager:on_received_traded_mod(part_id) + + local success, err = pcall(function() + + -- Find mod using part id + for k, v in pairs( tweak_data.blackmarket["weapon_mods"] ) do + if k == part_id then + + -- Get global value and add mod + local global_value = v.global_value or v.dlc or "normal" + Print("global_value: " .. global_value) + managers.blackmarket:add_to_inventory(global_value, "weapon_mods", part_id, true) + + end + end + + end) + if not success then Print("[Error] " .. err) end + +end + +function BlackMarketManager:get_mask_slot_data(mask) + + local category = "masks" + if not Global.blackmarket_manager.crafted_items[category] then + return nil + end + + local slot + if type(mask) == "table" then + slot = mask.slot + end + if type(mask) == "number" then + slot = mask + end + + return Global.blackmarket_manager.crafted_items[category][slot] + +end + +function BlackMarketManager:get_mask_data(mask_id) + return tweak_data.blackmarket.masks[mask_id] +end + +function BlackMarketManager:get_mask_name(mask) + return managers.localization:text(tweak_data.blackmarket.masks[mask].name_id) +end + +function BlackMarketManager:get_mask_mod_data(mod_id, category) + return tweak_data.blackmarket[category][mod_id] +end + +function BlackMarketManager:get_mask_mod_name(mod, category) + return managers.localization:text(tweak_data.blackmarket[category][mod].name_id) +end + +function BlackMarketManager:get_free_mask_slot() + + local unlocked_mask_slots = Global.blackmarket_manager.unlocked_mask_slots + local mask_slots = Global.blackmarket_manager.crafted_items.masks + + for i = 1, #unlocked_mask_slots, 1 do + if mask_slots[i] == nil then + return i + end + end + + return nil + +end + +function BlackMarketManager:has_all_dlc_for_mask(mask_id) + + -- Get mask data + local mask_data = tweak_data.blackmarket.masks[mask_id] + if mask_data == nil then + return + end + + -- Check if user has DLC for mask + local dlc = mask_data.dlc + if dlc ~= nil then + if not managers.dlc:has_dlc(dlc) then + return dlc + end + end + + return true + +end + +function BlackMarketManager:has_all_dlc_for_mask_mod(mod_id, category) + + -- Get part data + local part_data = tweak_data.blackmarket[category][mod_id] + if part_data == nil then + return + end + local part_global_value = part_data.global_value or part_data.dlc or "normal" + + -- Check user has DLC + local dlc = part_data.dlc + if dlc ~= nil then + if not managers.dlc:has_dlc(dlc) then + return dlc + end + end + + return true + +end + +function BlackMarketManager:has_all_dlc_for_mask_and_parts(mask_id, material, pattern, color) + + -- Get mask part data + local mask_data = tweak_data.blackmarket.masks[mask_id] + local material_data = tweak_data.blackmarket.materials[material] + local pattern_data = tweak_data.blackmarket.textures[pattern] + local color_data = tweak_data.blackmarket.colors[color] + + if mask_data == nil then + return + end + if material_data == nil then + return + end + if pattern_data == nil then + return + end + if color_data == nil then + return + end + + -- Get DLCs + local dlcs_to_test = {} + if mask_data.dlc ~= nil then + table.insert( dlcs_to_test, mask_data.dlc ) + end + if material_data.dlc ~= nil then + table.insert( dlcs_to_test, material_data.dlc ) + end + if pattern_data.dlc ~= nil then + table.insert( dlcs_to_test, pattern_data.dlc ) + end + if color_data.dlc ~= nil then + table.insert( dlcs_to_test, color_data.dlc ) + end + + -- Test DLCs + local dlcs_failed = nil + for k, v in pairs(dlcs_to_test) do + if not managers.dlc:has_dlc(v) then + + if dlcs_failed == nil then + dlcs_failed = {} + end + + dlcs_failed[v] = true + + end + end + + -- Return result + if dlcs_failed ~= nil then + return dlcs_failed + end + return true + +end + +function BlackMarketManager:add_traded_mask_to_inventory(mask_id) + self:add_traded_mask_part_to_inventory(mask_id, "masks") +end + +function BlackMarketManager:add_traded_mask_part_to_inventory(part_id, category) + + -- Get part data + local part_data = tweak_data.blackmarket[category][part_id] + if part_data == nil then + return + end + local part_global_value = part_data.global_value or part_data.dlc or "normal" + if part_data.infamous then + part_global_value = "infamous" + end + + -- Add it to inventory + self:add_to_inventory(part_global_value, category, part_id, false) + +end + +function BlackMarketManager:add_traded_mask_to_free_slot(mask_id) + + -- Get free mask slot + local slot = self:get_free_mask_slot() + if slot == nil then + return + end + + -- Get mask data + local mask_data = tweak_data.blackmarket.masks[mask_id] + if mask_data == nil then + return + end + local mask_global_value = mask_data.global_value or mask_data.dlc or "normal" + + -- Add mask to inventory + self:on_buy_mask_to_inventory(mask_id, mask_global_value, slot) + +end + +function BlackMarketManager:add_traded_modded_mask_to_free_slot(mask_id, material, pattern, color) + + local success, err = pcall(function() + + -- Get free mask slot + local slot = self:get_free_mask_slot() + if slot == nil then + return + end + + -- Get mask part data + local mask_data = tweak_data.blackmarket.masks[mask_id] + local material_data = tweak_data.blackmarket.materials[material] + local pattern_data = tweak_data.blackmarket.textures[pattern] + local color_data = tweak_data.blackmarket.colors[color] + + if mask_data == nil then + return + end + if material_data == nil then + return + end + if pattern_data == nil then + return + end + if color_data == nil then + return + end + + -- Global values + local mask_global_value = mask_data.global_value or mask_data.dlc or "normal" + local material_global_value = material_data.global_value or material_data.dlc or "normal" + local pattern_global_value = pattern_data.global_value or pattern_data.dlc or "normal" + local color_global_value = color_data.global_value or color_data.dlc or "normal" + + -- Add mask to inventory + self:on_buy_mask_to_inventory(mask_id, mask_global_value, slot) + + -- Start customizing mask + self:start_customize_mask(slot) + + -- Setup customized mask data + self._customize_mask.slot = slot + self._customize_mask.materials = {id = material, global_value = material_global_value} + self._customize_mask.textures = {id = pattern, global_value = pattern_global_value} + self._customize_mask.colors = {id = color, global_value = color_global_value} + + -- Add cash to automatically craft mask + local amount = managers.money:get_mask_crafting_price_modified(self._customize_mask.mask_id, self._customize_mask.global_value, self:get_customized_mask_blueprint(), {}) + managers.money:_add_to_total(amount, {no_offshore = true}) + + -- Finish customizing mask and add to inventory + self:finish_customize_mask() + + end) + if not success then Print("[Error] " .. err) end + +end + +function BlackMarketManager:remove_mask_from_inventory(mask_slot) + + local category = mask_slot.category + local slot = mask_slot.slot + + managers.blackmarket:alter_global_value_item(mask_slot.global_value, category, slot, mask_slot.mask_id, INV_REMOVE) + Global.blackmarket_manager.crafted_items[category][slot] = nil + +end + +function BlackMarketManager:remove_mask_mod_from_inventory(mod_id, category) + local data = managers.blackmarket:get_mask_mod_data(mod_id, category) + local global_value = data.global_value or data.dlc or "normal" + managers.blackmarket:remove_item(global_value, category, mod_id) +end diff --git a/GoonMod/lua/BlackMarketTweakData.lua b/GoonMod/lua/BlackMarketTweakData.lua new file mode 100644 index 0000000..113b31c --- /dev/null +++ b/GoonMod/lua/BlackMarketTweakData.lua @@ -0,0 +1,8 @@ + +CloneClass( BlackMarketTweakData ) + +Hooks:RegisterHook("BlackMarketTweakDataPostInitGrenades") +function BlackMarketTweakData._init_grenades(self) + self.orig._init_grenades(self) + Hooks:Call("BlackMarketTweakDataPostInitGrenades", self) +end diff --git a/GoonMod/lua/CharacterTweakData.lua b/GoonMod/lua/CharacterTweakData.lua new file mode 100644 index 0000000..66a9cae --- /dev/null +++ b/GoonMod/lua/CharacterTweakData.lua @@ -0,0 +1,92 @@ + +CloneClass( CharacterTweakData ) + +Hooks:RegisterHook("CharacterTweakDataPostMultiplyAllSpeeds") +function CharacterTweakData._multiply_all_speeds(this, walk_mul, run_mul) + this.orig._multiply_all_speeds(this, walk_mul, run_mul) + Hooks:Call("CharacterTweakDataPostMultiplyAllSpeeds", this, walk_mul, run_mul) +end + +Hooks:RegisterHook("CharacterTweakDataPostInitSecurity") +function CharacterTweakData._init_security(this, presets) + this.orig._init_security(this, presets) + Hooks:Call("CharacterTweakDataPostInitSecurity", this, presets) +end + +Hooks:RegisterHook("CharacterTweakDataPostInitGenSec") +function CharacterTweakData._init_gensec(this, presets) + this.orig._init_gensec(this, presets) + Hooks:Call("CharacterTweakDataPostInitGenSec", this, presets) +end + +Hooks:RegisterHook("CharacterTweakDataPostInitCop") +function CharacterTweakData._init_cop(this, presets) + this.orig._init_cop(this, presets) + Hooks:Call("CharacterTweakDataPostInitCop", this, presets) +end + +Hooks:RegisterHook("CharacterTweakDataPostInitFBI") +function CharacterTweakData._init_fbi(this, presets) + this.orig._init_fbi(this, presets) + Hooks:Call("CharacterTweakDataPostInitFBI", this, presets) +end + +Hooks:RegisterHook("CharacterTweakDataPostInitSWAT") +function CharacterTweakData._init_swat(this, presets) + this.orig._init_swat(this, presets) + Hooks:Call("CharacterTweakDataPostInitSWAT", this, presets) +end + +Hooks:RegisterHook("CharacterTweakDataPostInitHeavySWAT") +function CharacterTweakData._init_heavy_swat(this, presets) + this.orig._init_heavy_swat(this, presets) + Hooks:Call("CharacterTweakDataPostInitHeavySWAT", this, presets) +end + +Hooks:RegisterHook("CharacterTweakDataPostInitFBISWAT") +function CharacterTweakData._init_fbi_swat(this, presets) + this.orig._init_fbi_swat(this, presets) + Hooks:Call("CharacterTweakDataPostInitFBISWAT", this, presets) +end + +Hooks:RegisterHook("CharacterTweakDataPostInitFBIHeavySWAT") +function CharacterTweakData._init_fbi_heavy_swat(this, presets) + this.orig._init_fbi_heavy_swat(this, presets) + Hooks:Call("CharacterTweakDataPostInitFBIHeavySWAT", this, presets) +end + +Hooks:RegisterHook("CharacterTweakDataPostInitCitySWAT") +function CharacterTweakData._init_city_swat(this, presets) + this.orig._init_city_swat(this, presets) + Hooks:Call("CharacterTweakDataPostInitCitySWAT", this, presets) +end + +Hooks:RegisterHook("CharacterTweakDataPostInitSniper") +function CharacterTweakData._init_sniper(this, presets) + this.orig._init_sniper(this, presets) + Hooks:Call("CharacterTweakDataPostInitSniper", this, presets) +end + +Hooks:RegisterHook("CharacterTweakDataPostInitTank") +function CharacterTweakData._init_tank(this, presets) + this.orig._init_tank(this, presets) + Hooks:Call("CharacterTweakDataPostInitTank", this, presets) +end + +Hooks:RegisterHook("CharacterTweakDataPostInitCloaker") +function CharacterTweakData._init_spooc(this, presets) + this.orig._init_spooc(this, presets) + Hooks:Call("CharacterTweakDataPostInitCloaker", this, presets) +end + +Hooks:RegisterHook("CharacterTweakDataPostInitShield") +function CharacterTweakData._init_shield(this, presets) + this.orig._init_shield(this, presets) + Hooks:Call("CharacterTweakDataPostInitShield", this, presets) +end + +Hooks:RegisterHook("CharacterTweakDataPostInitTaser") +function CharacterTweakData._init_taser(this, presets) + this.orig._init_taser(this, presets) + Hooks:Call("CharacterTweakDataPostInitTaser", this, presets) +end diff --git a/GoonMod/lua/ChatManager.lua b/GoonMod/lua/ChatManager.lua new file mode 100644 index 0000000..dea327d --- /dev/null +++ b/GoonMod/lua/ChatManager.lua @@ -0,0 +1,14 @@ + +CloneClass( ChatManager ) + +Hooks:RegisterHook( "ChatManagerOnSendMessage" ) +function ChatManager.send_message(this, channel_id, sender, message) + Hooks:Call( "ChatManagerOnSendMessage", channel_id, sender, message ) + this.orig.send_message(this, channel_id, sender, message) +end + +Hooks:RegisterHook( "ChatManagerOnReceiveMessage" ) +function ChatManager._receive_message(this, channel_id, name, message, color, icon) + Hooks:Call( "ChatManagerOnReceiveMessage", channel_id, name, message, color, icon ) + this.orig._receive_message(this, channel_id, name, message, color, icon) +end diff --git a/GoonMod/lua/ContourExt.lua b/GoonMod/lua/ContourExt.lua new file mode 100644 index 0000000..233c5e9 --- /dev/null +++ b/GoonMod/lua/ContourExt.lua @@ -0,0 +1,19 @@ + +CloneClass( ContourExt ) + +Hooks:RegisterHook("ContourExtPreInitialize") +Hooks:RegisterHook("ContourExtPostInitialize") +function ContourExt.init(self, unit) + Hooks:Call("ContourExtPreInitialize", self, unit) + self.orig.init(self, unit) + Hooks:Call("ContourExtPostInitialize", self, unit) +end + +Hooks:RegisterHook("ContourExtPreAdd") +function ContourExt.add(self, type, sync, multiplier) + local r = Hooks:ReturnCall("ContourExtPreAdd", self, type, sync, multiplier) + if r ~= nil then + return + end + return self.orig.add(self, type, sync, multiplier) +end diff --git a/GoonMod/lua/CopActionHurt.lua b/GoonMod/lua/CopActionHurt.lua new file mode 100644 index 0000000..338b08c --- /dev/null +++ b/GoonMod/lua/CopActionHurt.lua @@ -0,0 +1,8 @@ + +CloneClass( CopActionHurt ) + +Hooks:RegisterHook("CopActionHurtPostUpdateRagdolled") +function CopActionHurt._upd_ragdolled(self, t) + self.orig._upd_ragdolled(self, t) + Hooks:Call("CopActionHurtPostUpdateRagdolled", self, t) +end diff --git a/GoonMod/lua/CopDamage.lua b/GoonMod/lua/CopDamage.lua new file mode 100644 index 0000000..982ff99 --- /dev/null +++ b/GoonMod/lua/CopDamage.lua @@ -0,0 +1,39 @@ + +CloneClass( CopDamage ) + +Hooks:RegisterHook("CopDamagePostInitialize") +function CopDamage.init(self, unit) + self.orig.init(self, unit) + Hooks:Call("CopDamagePostInitialize", self, unit) +end + +Hooks:RegisterHook("CopDamageSetMoverCollisionState") +function CopDamage.set_mover_collision_state(self, state) + local r = Hooks:ReturnCall("CopDamageSetMoverCollisionState", self, state) + if r ~= nil then + state = r + end + self.orig.set_mover_collision_state(self, state) +end + +Hooks:RegisterHook("CopDamagePreDamageExplosion") +function CopDamage.damage_explosion(self, attack_data) + local r = Hooks:ReturnCall("CopDamagePreDamageExplosion", self, attack_data) + if r ~= nil then + return + end + self.orig.damage_explosion(self, attack_data) +end + +Hooks:RegisterHook("CopDamagePostDeath") +function CopDamage.die(self, variant) + self.orig.die(self, variant) + Hooks:Call("CopDamagePostDeath", self, variant) +end + +Hooks:RegisterHook("CopDamagePostDamageBullet") +function CopDamage.damage_bullet(self, attack_data) + local res = self.orig.damage_bullet(self, attack_data) + Hooks:Call("CopDamagePostDamageBullet", self, attack_data, res) + return res +end diff --git a/GoonMod/lua/CopInventory.lua b/GoonMod/lua/CopInventory.lua new file mode 100644 index 0000000..00e037c --- /dev/null +++ b/GoonMod/lua/CopInventory.lua @@ -0,0 +1,50 @@ + +CloneClass( CopInventory ) + +Hooks:RegisterHook("CopInventoryDropShield") +function CopInventory.drop_shield(self) + + if alive(self._shield_unit) then + self._shield_unit:unlink() + if self._shield_unit:damage() then + self._shield_unit:damage():run_sequence_simple("enable_body") + end + end + + Hooks:Call("CopInventoryDropShield", self) + +end + +Hooks:RegisterHook("CopInventoryDestroyAllItems") +function CopInventory.destroy_all_items(self) + + CopInventory.super.destroy_all_items(self) + if alive(self._shield_unit) then + self._shield_unit:set_slot(0) + self._shield_unit = nil + end + + Hooks:Call("CopInventoryDestroyAllItems", self) + +end + +Hooks:RegisterHook("CopInventoryCheckSpawnShield") +function CopInventory._chk_spawn_shield(self, weapon_unit) + + -- self.orig._chk_spawn_shield(self, weapon_unit) + Hooks:Call("CopInventoryCheckSpawnShield", self, weapon_unit) + + if self._shield_unit_name and not alive(self._shield_unit) then + local align_name = self._shield_attach_point or Idstring("a_weapon_left_front") + local align_obj = self._unit:get_object(align_name) + self._shield_unit = World:spawn_unit(Idstring(self._shield_unit_name), align_obj:position(), align_obj:rotation()) + self._unit:link(align_name, self._shield_unit, self._shield_unit:orientation_object():name()) + self._shield_unit:set_enabled(false) + end + +end + +Hooks:RegisterHook("CopInventoryUpdate") +function CopInventory.update(self, unit, t, dt) + Hooks:Call("CopInventoryUpdate", self, unit, t, dt) +end diff --git a/GoonMod/lua/CoreMenuInput.lua b/GoonMod/lua/CoreMenuInput.lua new file mode 100644 index 0000000..ebbda5b --- /dev/null +++ b/GoonMod/lua/CoreMenuInput.lua @@ -0,0 +1,40 @@ + +core:import("CoreMenuInput") + +CloneClass( CoreMenuInput.MenuInput ) +local MenuInput = CoreMenuInput.MenuInput + +function MenuInput.input_slider(self, item, controller) + + local slider_delay_down = 0.1 + local slider_delay_pressed = 0.2 + + if self:menu_right_input_bool() then + + item:increase() + self._logic:trigger_item(true, item) + self:set_axis_x_timer(slider_delay_down) + if self:menu_right_pressed() then + local percentage = item:percentage() + if percentage > 0 and percentage < 100 then + self:post_event("slider_increase") + end + self:set_axis_x_timer(slider_delay_pressed) + end + + elseif self:menu_left_input_bool() then + + item:decrease() + self._logic:trigger_item(true, item) + self:set_axis_x_timer(slider_delay_down) + if self:menu_left_pressed() then + self:set_axis_x_timer(slider_delay_pressed) + local percentage = item:percentage() + if percentage > 0 and percentage < 100 then + self:post_event("slider_decrease") + end + end + + end + +end diff --git a/GoonMod/lua/CoreMenuItem.lua b/GoonMod/lua/CoreMenuItem.lua new file mode 100644 index 0000000..9a8bf1f --- /dev/null +++ b/GoonMod/lua/CoreMenuItem.lua @@ -0,0 +1,19 @@ + +core:import("CoreMenuItem") + +CloneClass( CoreMenuItem.Item ) +local Item = CoreMenuItem.Item + +function Item.trigger(self) + self.orig.trigger(self) +end + +function Item.dirty(self) + + if self._parameters.type ~= "CoreMenuItemSlider.ItemSlider" or self._type ~= "slider" then + if self.dirty_callback then + self.dirty_callback(self) + end + end + +end diff --git a/GoonMod/lua/CoreMenuItemSlider.lua b/GoonMod/lua/CoreMenuItemSlider.lua new file mode 100644 index 0000000..6877a68 --- /dev/null +++ b/GoonMod/lua/CoreMenuItemSlider.lua @@ -0,0 +1,24 @@ + +core:module("CoreMenuItemSlider") +local CloneClass = _G.CloneClass +local tweak_data = _G.tweak_data + +CloneClass( ItemSlider ) + +function ItemSlider.setup_gui(self, node, row_item) + local r = self.orig.setup_gui(self, node, row_item) + row_item.gui_slider_text:set_font_size( tweak_data.menu.stats_font_size ) + return r +end + +function ItemSlider.set_value(self, value) + self._value = math.min(math.max(self._min, value), self._max) + self:dirty() +end + +function ItemSlider.reload(self, row_item, node) + local r = self.orig.reload(self, row_item, node) + local value = self:show_value() and string.format("%.2f", math.round_with_precision(self:value(), 2)) or string.format("%.0f", self:percentage()) .. "%" + row_item.gui_slider_text:set_text(value) + return r +end diff --git a/GoonMod/lua/CoreMenuLogic.lua b/GoonMod/lua/CoreMenuLogic.lua new file mode 100644 index 0000000..f5f0f7f --- /dev/null +++ b/GoonMod/lua/CoreMenuLogic.lua @@ -0,0 +1,25 @@ + +core:import("CoreMenuLogic") + +CloneClass( CoreMenuLogic.Logic ) +local Logic = CoreMenuLogic.Logic + +function Logic.select_node(self, node_name, queue, ...) + -- Print("Logic.select_node") + self.orig.select_node(self, node_name, queue, ...) +end + +function Logic.select_item(self, item_name, queue) + -- Print("Logic.select_item") + self.orig.select_item(self, item_name, queue) +end + +function Logic.trigger_item(self, queue, item) + -- Print("Logic.trigger_item") + self.orig.trigger_item(self, queue, item) +end + +function Logic._trigger_item(self, item) + -- Print("Logic._trigger_item") + self.orig._trigger_item(self, item) +end diff --git a/GoonMod/lua/CoreMissionManager.lua b/GoonMod/lua/CoreMissionManager.lua new file mode 100644 index 0000000..d344a8d --- /dev/null +++ b/GoonMod/lua/CoreMissionManager.lua @@ -0,0 +1,108 @@ +core:module("CoreMissionManager") + +local function PrintTable (tbl, cmp) + cmp = cmp or {} + if type(tbl) == "table" then + for k, v in pairs (tbl) do + if type(v) == "table" and not cmp[v] then + cmp[v] = true + Print( string.format("[\"%s\"] -> table", tostring(k)) ); + PrintTable (v, cmp) + else + Print( string.format("\"%s\" -> %s", tostring(k), tostring(v)) ) + end + end + else Print(tbl) end +end + +local function DoSaveTable(tbl, cmp, fileName, fileIsOpen, preText) + + local file = nil + if fileIsOpen == nil then + file = io.open(fileName, "w") + else + file = fileIsOpen + end + + cmp = cmp or {} + if type(tbl) == "table" then + for k, v in pairs(tbl) do + if type(v) == "table" and not cmp[v] then + cmp[v] = true + file:write( preText .. string.format("[\"%s\"] -> table", tostring (k)) .. "\n" ) + DoSaveTable(v, cmp, fileName, file, preText .. "\t") + else + file:write( preText .. string.format( "\"%s\" -> %s", tostring(k), tostring(v) ) .. "\n" ) + end + end + else + file:write( preText .. tbl .. "\n") + end + + if fileIsOpen == nil then + file:close() + end +end + +local function SaveTable(tbl, file) + DoSaveTable(tbl, {}, file, nil, "") +end + +function MissionManager:parse(params, stage_name, offset, file_type) + + Print("MissionManager:parse") + + local file_path, activate_mission + if CoreClass.type_name(params) == "table" then + file_path = params.file_path + file_type = params.file_type or "mission" + activate_mission = params.activate_mission + offset = params.offset + else + file_path = params + file_type = file_type or "mission" + end + + Print("-----") + Print("file_path: ", file_path) + Print("file_type: ", file_type) + Print("activate_mission: ", activate_mission) + Print("offset: ", offset) + Print("-----") + + CoreDebug.cat_debug("gaspode", "MissionManager", file_path, file_type, activate_mission) + if not DB:has(file_type, file_path) then + Application:error("Couldn't find", file_path, "(", file_type, ")") + return false + end + + local reverse = string.reverse(file_path) + local i = string.find(reverse, "/") + local file_dir = string.reverse(string.sub(reverse, i)) + local continent_files = self:_serialize_to_script(file_type, file_path) + continent_files._meta = nil + + for name, data in pairs(continent_files) do + if not managers.worlddefinition:continent_excluded(name) then + self:_load_mission_file(file_dir, data) + end + end + + self:_activate_mission(activate_mission) + + return true + +end + +function MissionManager:_load_mission_file(file_dir, data) + + local file_path = file_dir .. data.file + local scripts = self:_serialize_to_script("mission", file_path) + scripts._meta = nil + + for name, data in pairs(scripts) do + data.name = name + self:_add_script(data) + end + +end diff --git a/GoonMod/lua/CriminalsManager.lua b/GoonMod/lua/CriminalsManager.lua new file mode 100644 index 0000000..034712b --- /dev/null +++ b/GoonMod/lua/CriminalsManager.lua @@ -0,0 +1,12 @@ + +CloneClass( CriminalsManager ) + +Hooks:Call("CriminalsManagerNumberOfTakenCriminals") +function CriminalsManager.nr_taken_criminals(self) + local orig = self.orig.nr_taken_criminals(self) + local r = Hooks:ReturnCall("CriminalsManagerNumberOfTakenCriminals", self) + if r ~= nil then + return r + end + return orig +end diff --git a/GoonMod/lua/ECMJammerBase.lua b/GoonMod/lua/ECMJammerBase.lua new file mode 100644 index 0000000..a64fc80 --- /dev/null +++ b/GoonMod/lua/ECMJammerBase.lua @@ -0,0 +1,146 @@ + +CloneClass( ECMJammerBase ) + +function ECMJammerBase:init(unit) + + UnitBase.init(self, unit, true) + + self._unit = unit + self._position = self._unit:position() + self._rotation = self._unit:rotation() + self._g_glow_jammer_green = self._unit:get_object(Idstring("g_glow_func1_green")) + self._g_glow_jammer_red = self._unit:get_object(Idstring("g_glow_func1_red")) + self._g_glow_feedback_green = self._unit:get_object(Idstring("g_glow_func2_green")) + self._g_glow_feedback_red = self._unit:get_object(Idstring("g_glow_func2_red")) + self._max_battery_life = tweak_data.upgrades.ecm_jammer_base_battery_life + self._battery_life = 0.01 + self._low_battery_life = tweak_data.upgrades.ecm_jammer_base_low_battery_life + self._feedback_active = false + self._jammer_active = false + + if Network:is_client() then + self._validate_clbk_id = "ecm_jammer_validate" .. tostring(unit:key()) + managers.enemy:add_delayed_clbk(self._validate_clbk_id, callback(self, self, "_clbk_validate"), Application:time() + 60) + end + +end + +function ECMJammerBase:setup(battery_life_upgrade_lvl, owner) + + self._slotmask = managers.slot:get_mask("trip_mine_targets") + self._max_battery_life = tweak_data.upgrades.ecm_jammer_base_battery_life * battery_life_upgrade_lvl + self._battery_life = 0.01 + self._owner = owner + self._owner_id = owner and managers.network:game():member_from_unit(owner):peer():id() + + self:spawn_workspace() + +end + +function ECMJammerBase:spawn_workspace() + + local w, h = 320, 320 + local scale = 1 + local scaled_w, scaled_h = w * scale, h * scale + local pos, rot = self._position + Vector3((scaled_w / 2), -(scaled_h / 2), 1), self._rotation + + local new_gui = World:newgui() + self._workspace = new_gui:create_world_workspace(w, h, pos, Vector3(-scaled_w, 0, 0):rotate_with(rot), Vector3(0, 0, -scaled_h):rotate_with(rot)) + + self._circle_1 = CircleBitmapGuiObject:new(self._workspace:panel(), { + use_bg = false, + radius = self._workspace:panel():w() / 2, + sides = 64, + current = 64, + total = 64, + color = Color.white, + alpha = 1, + blend_mode = "add", + image = "guis/textures/pd2/specialization/progress_ring", + layer = 2 + }) + self._circle_1:set_current( 0.66 ) + self._circle_1_rotation = math.random(0, 360) + self._circle_1._circle:set_rotation( self._circle_1_rotation ) + +end + +function ECMJammerBase:set_battery_low() +end + +function ECMJammerBase:set_feedback_active() + + if not managers.network:session() then + return + end + + if not ECMJammerBase._teleport_destination then + ECMJammerBase._teleport_destination = self._position + else + self:teleport() + end + +end + +function ECMJammerBase:teleport() + + local hit_pos = self._position + local range = 400 + local slotmask = managers.slot:get_mask("explosion_targets") + local bodies = World:find_bodies("intersect", "sphere", hit_pos, range, slotmask) + + if not self._teleported_units then + self._teleported_units = {} + end + + for _, body in ipairs(bodies) do + local character = body:unit() + if character:carry_data() and body:collisions_enabled() then + + body:set_keyframed() + -- body:set_collisions_enabled(false) + character:set_position( ECMJammerBase._teleport_destination + Vector3(0, 0, 100) ) + + table.insert( self._teleported_units, character ) + + end + end + self._teleported_units_time = Application:time() + + ECMJammerBase._teleport_destination = nil + +end + +function ECMJammerBase:update(unit, t, dt) + + self.orig.update(self, unit, t, dt) + + self:update_teleported_units(unit, t, dt) + self:update_rotations(unit, t, dt) + +end + +function ECMJammerBase:update_teleported_units(unit, t, dt) + + if self._teleported_units and Application:time() - self._teleported_units_time > 0.5 then + + local bounds = 1000000 + for k, v in pairs( self._teleported_units ) do + v:set_dynamic() + v:set_collisions_enabled(true) + v:push(100, math.UP * 600) + end + self._teleported_units = nil + + end + +end + +function ECMJammerBase:update_rotations(unit, t, dt) + + if self._circle_1 then + self._circle_1_rotation = self._circle_1_rotation + dt * 10 + self._circle_1._circle:set_rotation( self._circle_1_rotation ) + end + +end diff --git a/GoonMod/lua/ElementLaserTrigger.lua b/GoonMod/lua/ElementLaserTrigger.lua new file mode 100644 index 0000000..682fa1d --- /dev/null +++ b/GoonMod/lua/ElementLaserTrigger.lua @@ -0,0 +1,14 @@ + +CloneClass( ElementLaserTrigger ) + +Hooks:RegisterHook("ElementLaserTriggerPostInit") +function ElementLaserTrigger.init(self, ...) + self.orig.init(self, ...) + Hooks:Call("ElementLaserTriggerPostInit", self) +end + +Hooks:RegisterHook("ElementLaserTriggerUpdateDraw") +function ElementLaserTrigger.update_laser_draw(self, t, dt) + self.orig.update_laser_draw(self, t, dt) + Hooks:Call("ElementLaserTriggerUpdateDraw", self, t, dt) +end diff --git a/GoonMod/lua/ElementSpawnGrenade.lua b/GoonMod/lua/ElementSpawnGrenade.lua new file mode 100644 index 0000000..e4562cf --- /dev/null +++ b/GoonMod/lua/ElementSpawnGrenade.lua @@ -0,0 +1,6 @@ + +CloneClass( ElementSpawnGrenade ) + +function ElementSpawnGrenade.on_executed(self, instigator) + self.orig.on_executed(self, instigator) +end diff --git a/GoonMod/lua/EnemyManager.lua b/GoonMod/lua/EnemyManager.lua new file mode 100644 index 0000000..555ba49 --- /dev/null +++ b/GoonMod/lua/EnemyManager.lua @@ -0,0 +1,14 @@ + +CloneClass( EnemyManager ) + +Hooks:RegisterHook( "EnemyManagerPreUpdateCorpseDisposal" ) +function EnemyManager._upd_corpse_disposal(this) + Hooks:Call("EnemyManagerPreUpdateCorpseDisposal", this) + this.orig._upd_corpse_disposal(this) +end + +Hooks:RegisterHook("EnemyManagerInitEnemyData") +function EnemyManager._init_enemy_data(self) + self.orig._init_enemy_data(self) + Hooks:Call("EnemyManagerInitEnemyData", self) +end diff --git a/GoonMod/lua/FPCameraPlayerBase.lua b/GoonMod/lua/FPCameraPlayerBase.lua new file mode 100644 index 0000000..91bacc5 --- /dev/null +++ b/GoonMod/lua/FPCameraPlayerBase.lua @@ -0,0 +1,8 @@ + +CloneClass( FPCameraPlayerBase ) + +Hooks:RegisterHook("FPCameraBaseStanceEnteredCallback") +function FPCameraPlayerBase.clbk_stance_entered(self, new_shoulder_stance, new_head_stance, new_vel_overshot, new_fov, new_shakers, stance_mod, duration_multiplier, duration) + Hooks:Call( "FPCameraBaseStanceEnteredCallback", self, new_shoulder_stance, new_head_stance, new_vel_overshot, new_fov, new_shakers, stance_mod, duration_multiplier, duration ) + self.orig.clbk_stance_entered(self, new_shoulder_stance, new_head_stance, new_vel_overshot, new_fov, new_shakers, stance_mod, duration_multiplier, duration) +end diff --git a/GoonMod/lua/FragGrenade.lua b/GoonMod/lua/FragGrenade.lua new file mode 100644 index 0000000..0b16727 --- /dev/null +++ b/GoonMod/lua/FragGrenade.lua @@ -0,0 +1,14 @@ + +CloneClass( FragGrenade ) + +Hooks:RegisterHook("FragGrenadePostInit") +function FragGrenade.init(self, unit) + self.orig.init(self, unit) + Hooks:Call("FragGrenadePostInit", self, unit) +end + +Hooks:RegisterHook("FragGrenadeDetonate") +function FragGrenade._detonate(self) + self._detonate(self) + Hooks:Call("FragGrenadeDetonate") +end diff --git a/GoonMod/lua/GageAssignmentManager.lua b/GoonMod/lua/GageAssignmentManager.lua new file mode 100644 index 0000000..7eacde4 --- /dev/null +++ b/GoonMod/lua/GageAssignmentManager.lua @@ -0,0 +1,8 @@ + +CloneClass( GageAssignmentManager ) + +Hooks:RegisterHook("GageAssignmentManagerOnMissionCompleted") +function GageAssignmentManager.on_mission_completed(self) + Hooks:Call("GageAssignmentManagerOnMissionCompleted", self) + return self.orig.on_mission_completed(self) +end diff --git a/GoonMod/lua/GameSetup.lua b/GoonMod/lua/GameSetup.lua new file mode 100644 index 0000000..f842b58 --- /dev/null +++ b/GoonMod/lua/GameSetup.lua @@ -0,0 +1,8 @@ + +CloneClass( GameSetup ) + +Hooks:RegisterHook("GameSetupUpdate") +function GameSetup.update(this, t, dt) + Hooks:Call("GameSetupUpdate", t, dt) + return this.orig.update(this, t, dt) +end diff --git a/GoonMod/lua/GameStateMachine.lua b/GoonMod/lua/GameStateMachine.lua new file mode 100644 index 0000000..6daf384 --- /dev/null +++ b/GoonMod/lua/GameStateMachine.lua @@ -0,0 +1,8 @@ + +CloneClass( GameStateMachine ) + +Hooks:RegisterHook("GameStateMachineChangeStateByName") +function GameStateMachine.change_state_by_name(self, state_name, params) + Hooks:Call("GameStateMachineChangeStateByName", self, state_name, params) + self.orig.change_state_by_name(self, state_name, params) +end diff --git a/GoonMod/lua/GroupAIManager.lua b/GoonMod/lua/GroupAIManager.lua new file mode 100644 index 0000000..ced9537 --- /dev/null +++ b/GoonMod/lua/GroupAIManager.lua @@ -0,0 +1,7 @@ + +CloneClass( GroupAIManager ) + +function GroupAIManager.set_state(self, name) + -- Print("Setting Group AI State to: " .. name) + self.orig.set_state(self, name) +end diff --git a/GoonMod/lua/GroupAIStateBase.lua b/GoonMod/lua/GroupAIStateBase.lua new file mode 100644 index 0000000..d223042 --- /dev/null +++ b/GoonMod/lua/GroupAIStateBase.lua @@ -0,0 +1,15 @@ + +CloneClass( GroupAIStateBase ) + +function GroupAIStateBase.convert_hostage_to_criminal(self, unit, peer_unit) + unit:movement()._preconvert_team = unit:movement():team() + self.orig.convert_hostage_to_criminal(self, unit, peer_unit) +end + +function GroupAIStateBase.clbk_minion_dies(self, player_key, minion_unit, damage_info) + local _preconvert_team = minion_unit:movement()._preconvert_team + if _preconvert_team ~= nil then + minion_unit:movement():set_team( _preconvert_team ) + end + self.orig.clbk_minion_dies(self, player_key, minion_unit, damage_info) +end diff --git a/GoonMod/lua/GroupAIStateBesiege.lua b/GoonMod/lua/GroupAIStateBesiege.lua new file mode 100644 index 0000000..2301e2a --- /dev/null +++ b/GoonMod/lua/GroupAIStateBesiege.lua @@ -0,0 +1,529 @@ + +CloneClass( GroupAIStateBesiege ) + +Hooks:RegisterHook("GroupAIStateBesiegeInit") +function GroupAIStateBesiege.init(self) + self.orig.init(self) + Hooks:Call("GroupAIStateBesiegeInit", self) +end + +--[[ +function GroupAIStateBesiege:_assign_groups_to_retire(allowed_groups, suitable_grp_func) + -- Never back down +end + +function GroupAIStateBesiege:_upd_group_spawning() + + -- Print("Update spawn groups, num groups: ", #self._spawning_groups) + for k, v in pairs( self._spawning_groups ) do + self:_update_individual_group_spawning(k) + end + +end + +function GroupAIStateBesiege:_queue_police_upd_task() + self._police_upd_task_queued = true + managers.enemy:queue_task("GroupAIStateBesiege._upd_police_activity", GroupAIStateBesiege._upd_police_activity, self, self._t + (next(self._spawning_groups) and 0.0166 or 2)) +end + +function GroupAIStateBesiege:_update_individual_group_spawning(id) + + local spawn_task = self._spawning_groups[id] + if not spawn_task then + return + end + local nr_units_spawned = 0 + local produce_data = { + name = true, + spawn_ai = {} + } + local group_ai_tweak = tweak_data.group_ai + local spawn_points = spawn_task.spawn_group.spawn_pts + local function _try_spawn_unit(u_type_name, spawn_entry) + if nr_units_spawned >= GroupAIStateBesiege._MAX_SIMULTANEOUS_SPAWNS then + return + end + local hopeless = true + for _, sp_data in ipairs(spawn_points) do + local category = group_ai_tweak.unit_categories[u_type_name] + if (sp_data.accessibility == "any" or category.access[sp_data.accessibility]) and (not sp_data.amount or sp_data.amount > 0) and sp_data.mission_element:enabled() then + hopeless = false + if self._t > sp_data.delay_t then + produce_data.name = category.units[math.random(#category.units)] + local spawned_unit = sp_data.mission_element:produce(produce_data) + local u_key = spawned_unit:key() + local objective + if spawn_task.objective then + objective = self.clone_objective(spawn_task.objective) + else + objective = spawn_task.group.objective.element:get_random_SO(spawned_unit) + if not objective then + spawned_unit:set_slot(0) + return true + end + objective.grp_objective = spawn_task.group.objective + end + local u_data = self._police[u_key] + self:set_enemy_assigned(objective.area, u_key) + if spawn_entry.tactics then + u_data.tactics = spawn_entry.tactics + u_data.tactics_map = {} + for _, tactic_name in ipairs(u_data.tactics) do + u_data.tactics_map[tactic_name] = true + end + end + spawned_unit:brain():set_spawn_entry(spawn_entry, u_data.tactics_map) + u_data.rank = spawn_entry.rank + self:_add_group_member(spawn_task.group, u_key) + if spawned_unit:brain():is_available_for_assignment(objective) then + if objective.element then + objective.element:clbk_objective_administered(spawned_unit) + end + spawned_unit:brain():set_objective(objective) + else + spawned_unit:brain():set_followup_objective(objective) + end + nr_units_spawned = nr_units_spawned + 1 + if spawn_task.ai_task then + spawn_task.ai_task.force_spawned = spawn_task.ai_task.force_spawned + 1 + end + sp_data.delay_t = self._t + sp_data.interval + if sp_data.amount then + sp_data.amount = sp_data.amount - 1 + end + return true + end + end + end + if hopeless then + debug_pause("[GroupAIStateBesiege:_upd_group_spawning] spawn group", spawn_task.spawn_group.id, "failed to spawn unit", u_type_name) + return true + end + end + + for u_type_name, spawn_info in pairs(spawn_task.units_remaining) do + if not group_ai_tweak.unit_categories[u_type_name].access.acrobatic then + for i = spawn_info.amount, 1, -1 do + local success = _try_spawn_unit(u_type_name, spawn_info.spawn_entry) + if success then + spawn_info.amount = spawn_info.amount - 1 + end + end + end + end + for u_type_name, spawn_info in pairs(spawn_task.units_remaining) do + for i = spawn_info.amount, 1, -1 do + local success = _try_spawn_unit(u_type_name, spawn_info.spawn_entry) + if success then + spawn_info.amount = spawn_info.amount - 1 + end + end + end + local complete = true + for u_type_name, spawn_info in pairs(spawn_task.units_remaining) do + if 0 < spawn_info.amount then + complete = false + else + end + end + if complete then + Print("Finished spawning group: ", tostring( spawn_task.group.id )) + spawn_task.group.has_spawned = true + table.remove(self._spawning_groups, 1) + if 0 >= spawn_task.group.size then + self._groups[spawn_task.group.id] = nil + end + end +end + +function GroupAIStateBesiege:_upd_recon_tasks() + local task_data = self._task_data.recon.tasks[1] + self:_assign_enemy_groups_to_recon() + if not task_data then + return + end + local t = self._t + self:_assign_assault_groups_to_retire() + local target_pos = task_data.target_area.pos + local nr_wanted = self:_get_difficulty_dependent_value(tweak_data.group_ai.besiege.recon.force) - self:_count_police_force("recon") + if nr_wanted <= 0 then + return + end + local used_event, used_spawn_points, reassigned + if task_data.use_spawn_event then + task_data.use_spawn_event = false + if self:_try_use_task_spawn_event(t, task_data.target_area, "recon") then + used_event = true + end + end + + local spawn_group, spawn_group_type = self:_find_spawn_group_near_area(task_data.target_area, tweak_data.group_ai.besiege.recon.groups, nil, nil, callback(self, self, "_verify_anticipation_spawn_point")) + if spawn_group then + local grp_objective = { + type = "recon_area", + area = spawn_group.area, + target_area = task_data.target_area, + attitude = "engage", + stance = "hos", + moving_in = true, + charge = true, + open_fire = true, + } + self:_spawn_in_group(spawn_group, spawn_group_type, grp_objective) + end + + if used_event or used_spawn_points or reassigned then + table.remove(self._task_data.recon.tasks, 1) + self._task_data.recon.next_dispatch_t = t + math.ceil(self:_get_difficulty_dependent_value(tweak_data.group_ai.besiege.recon.interval)) + math.random() * tweak_data.group_ai.besiege.recon.interval_variation + end +end + +function GroupAIStateBesiege:_upd_reenforce_tasks() + local reenforce_tasks = self._task_data.reenforce.tasks + local t = self._t + local i = #reenforce_tasks + while i > 0 do + local task_data = reenforce_tasks[i] + local force_settings = task_data.target_area.factors.force + local force_required = force_settings and force_settings.force + if force_required then + local force_occupied = 0 + for group_id, group in pairs(self._groups) do + if (group.objective.target_area or group.objective.area) == task_data.target_area and group.objective.type == "reenforce_area" then + force_occupied = force_occupied + (group.has_spawned and group.size or group.initial_size) + end + end + local undershot = force_required - force_occupied + if undershot > 0 and not self._task_data.regroup.active and self._task_data.assault.phase ~= "fade" and t > self._task_data.reenforce.next_dispatch_t and self:is_area_safe(task_data.target_area) then + local used_event + if task_data.use_spawn_event then + task_data.use_spawn_event = false + if self:_try_use_task_spawn_event(t, task_data.target_area, "reenforce") then + used_event = true + end + end + local used_group, spawning_groups + + local spawn_group, spawn_group_type = self:_find_spawn_group_near_area(task_data.target_area, tweak_data.group_ai.besiege.reenforce.groups, nil, nil, nil) + if spawn_group then + local grp_objective = { + type = "reenforce_area", + area = spawn_group.area, + target_area = task_data.target_area, + attitude = "engage", + stance = "hos", + pose = "stand", + moving_in = true, + charge = true, + open_fire = true, + } + self:_spawn_in_group(spawn_group, spawn_group_type, grp_objective) + used_group = true + end + + elseif undershot < 0 then + local force_defending = 0 + for group_id, group in pairs(self._groups) do + if group.objective.area == task_data.target_area and group.objective.type == "reenforce_area" then + force_defending = force_defending + (group.has_spawned and group.size or group.initial_size) + end + end + local overshot = force_defending - force_required + if overshot > 0 then + local closest_group, closest_group_size + for group_id, group in pairs(self._groups) do + if group.has_spawned then + if (group.objective.target_area or group.objective.area) == task_data.target_area and group.objective.type == "reenforce_area" and (not closest_group_size or closest_group_size < group.size) and overshot >= group.size then + closest_group = group + closest_group_size = group.size + end + end + end + if closest_group then + self:_assign_group_to_retire(closest_group) + end + end + end + else + for group_id, group in pairs(self._groups) do + if group.has_spawned then + if (group.objective.target_area or group.objective.area) == task_data.target_area and group.objective.type == "reenforce_area" then + self:_assign_group_to_retire(group) + end + end + end + reenforce_tasks[i] = reenforce_tasks[#reenforce_tasks] + table.remove(reenforce_tasks) + end + i = i - 1 + end + self:_assign_enemy_groups_to_reenforce() +end + +function GroupAIStateBesiege:_upd_assault_task() + local task_data = self._task_data.assault + if not task_data.active then + return + end + local t = self._t + self:_assign_recon_groups_to_retire() + local force_pool = self:_get_difficulty_dependent_value(tweak_data.group_ai.besiege.assault.force_pool) * self:_get_balancing_multiplier(tweak_data.group_ai.besiege.assault.force_pool_balance_mul) + local task_spawn_allowance = force_pool - (self._hunt_mode and 0 or task_data.force_spawned) + if task_data.phase == "anticipation" then + if task_spawn_allowance <= 0 then + task_data.phase = "fade" + task_data.phase_end_t = t + tweak_data.group_ai.besiege.assault.fade_duration + elseif t > task_data.phase_end_t or self._drama_data.zone == "high" then + managers.mission:call_global_event("start_assault") + managers.hud:start_assault() + self:_set_rescue_state(false) + task_data.phase = "build" + task_data.phase_end_t = self._t + tweak_data.group_ai.besiege.assault.build_duration + task_data.is_hesitating = nil + self:set_assault_mode(true) + managers.trade:set_trade_countdown(false) + else + managers.hud:check_anticipation_voice(task_data.phase_end_t - t) + managers.hud:check_start_anticipation_music(task_data.phase_end_t - t) + if task_data.is_hesitating and self._t > task_data.voice_delay then + if 0 < self._hostage_headcount then + local best_group + for _, group in pairs(self._groups) do + if not best_group or group.objective.type == "reenforce_area" then + best_group = group + elseif best_group.objective.type ~= "reenforce_area" and group.objective.type ~= "retire" then + best_group = group + end + end + if best_group and self:_voice_delay_assault(best_group) then + task_data.is_hesitating = nil + end + else + task_data.is_hesitating = nil + end + end + end + elseif task_data.phase == "build" then + if task_spawn_allowance <= 0 then + task_data.phase = "fade" + task_data.phase_end_t = t + tweak_data.group_ai.besiege.assault.fade_duration + elseif t > task_data.phase_end_t or self._drama_data.zone == "high" then + task_data.phase = "sustain" + task_data.phase_end_t = t + math.lerp(self:_get_difficulty_dependent_value(tweak_data.group_ai.besiege.assault.sustain_duration_min), self:_get_difficulty_dependent_value(tweak_data.group_ai.besiege.assault.sustain_duration_max), math.random()) * self:_get_balancing_multiplier(tweak_data.group_ai.besiege.assault.sustain_duration_balance_mul) + end + elseif task_data.phase == "sustain" then + if task_spawn_allowance <= 0 then + task_data.phase = "fade" + task_data.phase_end_t = t + tweak_data.group_ai.besiege.assault.fade_duration + elseif t > task_data.phase_end_t and not self._hunt_mode then + task_data.phase = "fade" + task_data.phase_end_t = t + tweak_data.group_ai.besiege.assault.fade_duration + end + else + local enemies_left = self:_count_police_force("assault") + if enemies_left < 7 or t > task_data.phase_end_t + 350 then + if t > task_data.phase_end_t - 8 and not task_data.said_retreat then + if self._drama_data.amount < tweak_data.drama.assault_fade_end then + task_data.said_retreat = true + self:_police_announce_retreat() + end + elseif t > task_data.phase_end_t and self._drama_data.amount < tweak_data.drama.assault_fade_end and self:_count_criminals_engaged_force(4) <= 3 then + task_data.active = nil + task_data.phase = nil + task_data.said_retreat = nil + if self._draw_drama then + self._draw_drama.assault_hist[#self._draw_drama.assault_hist][2] = t + end + managers.mission:call_global_event("end_assault") + self:_begin_regroup_task() + return + end + else + end + end + if self._drama_data.amount <= tweak_data.drama.low then + for criminal_key, criminal_data in pairs(self._player_criminals) do + self:criminal_spotted(criminal_data.unit) + for group_id, group in pairs(self._groups) do + if group.objective.charge then + for u_key, u_data in pairs(group.units) do + u_data.unit:brain():clbk_group_member_attention_identified(nil, criminal_key) + end + end + end + end + end + local primary_target_area = task_data.target_areas[1] + if self:is_area_safe(primary_target_area) then + local target_pos = primary_target_area.pos + local nearest_area, nearest_dis + for criminal_key, criminal_data in pairs(self._player_criminals) do + if not criminal_data.status then + local dis = mvector3.distance_sq(target_pos, criminal_data.m_pos) + if not nearest_dis or nearest_dis > dis then + nearest_dis = dis + nearest_area = self:get_area_from_nav_seg_id(criminal_data.tracker:nav_segment()) + end + end + end + if nearest_area then + primary_target_area = nearest_area + task_data.target_areas[1] = nearest_area + end + end + local nr_wanted = task_data.force - self:_count_police_force("assault") + if task_data.phase == "anticipation" then + nr_wanted = nr_wanted - 5 + end + if nr_wanted > 0 and task_data.phase ~= "fade" then + local used_event + if task_data.use_spawn_event and task_data.phase ~= "anticipation" then + task_data.use_spawn_event = false + if self:_try_use_task_spawn_event(t, primary_target_area, "assault") then + used_event = true + end + end + + local spawn_group, spawn_group_type = self:_find_spawn_group_near_area(primary_target_area, tweak_data.group_ai.besiege.assault.groups, nil, nil, nil) + if spawn_group then + local grp_objective = { + type = "assault_area", + area = spawn_group.area, + coarse_path = { + { + spawn_group.area.pos_nav_seg, + spawn_group.area.pos + } + }, + attitude = "engage", + pose = "crouch", + stance = "hos", + moving_in = true, + charge = true, + open_fire = true, + } + self:_spawn_in_group(spawn_group, spawn_group_type, grp_objective, task_data) + end + + end + if task_data.phase ~= "anticipation" then + if t > task_data.use_smoke_timer then + task_data.use_smoke = true + end + if self._smoke_grenade_queued and task_data.use_smoke and not self:is_smoke_grenade_active() then + self:detonate_smoke_grenade(self._smoke_grenade_queued[1], self._smoke_grenade_queued[1], self._smoke_grenade_queued[2], self._smoke_grenade_queued[4]) + if self._smoke_grenade_queued[3] then + self._smoke_grenade_ignore_control = true + end + end + end + self:_assign_enemy_groups_to_assault(task_data.phase) +end + +function GroupAIStateBesiege:_spawn_in_group(spawn_group, spawn_group_type, grp_objective, ai_task) + + local spawn_group_desc = tweak_data.group_ai.enemy_spawn_groups[spawn_group_type] + local wanted_nr_units + if type(spawn_group_desc.amount) == "number" then + wanted_nr_units = spawn_group_desc.amount + else + wanted_nr_units = math.random(spawn_group_desc.amount[1], spawn_group_desc.amount[2]) + end + local valid_unit_types = {} + self._extract_group_desc_structure(spawn_group_desc.spawn, valid_unit_types) + local function _get_special_unit_type_count(special_type) + if not self._special_units[special_type] then + return 0 + end + return table.size(self._special_units[special_type]) + end + + local unit_categories = tweak_data.group_ai.unit_categories + local total_wgt = 0 + local i = 1 + while i <= #valid_unit_types do + local spawn_entry = valid_unit_types[i] + local cat_data = unit_categories[spawn_entry.unit] + if not cat_data then + debug_pause("[GroupAIStateBesiege:_spawn_in_group] unit category doesn't exist:", spawn_entry.unit) + return + elseif cat_data.special_type and tweak_data.group_ai.special_unit_spawn_limits[cat_data.special_type] then + if _get_special_unit_type_count(cat_data.special_type) + (spawn_entry.amount_min or 0) > tweak_data.group_ai.special_unit_spawn_limits[cat_data.special_type] then + spawn_group.delay_t = 0 + return + end + else + total_wgt = total_wgt + spawn_entry.freq + i = i + 1 + end + end + local spawn_task = { + objective = not grp_objective.element and self._create_objective_from_group_objective(grp_objective), + units_remaining = {}, + spawn_group = spawn_group, + spawn_group_type = spawn_group_type, + ai_task = ai_task + } + Print("Adding group to spawning queue: ", spawn_group_type) + table.insert(self._spawning_groups, spawn_task) + local function _add_unit_type_to_spawn_task(i, spawn_entry) + local spawn_amount_mine = 1 + (spawn_task.units_remaining[spawn_entry.unit] and spawn_task.units_remaining[spawn_entry.unit].amount or 0) + spawn_task.units_remaining[spawn_entry.unit] = {amount = spawn_amount_mine, spawn_entry = spawn_entry} + wanted_nr_units = wanted_nr_units - 1 + if spawn_entry.amount_min then + spawn_entry.amount_min = spawn_entry.amount_min - 1 + end + if spawn_entry.amount_max then + spawn_entry.amount_max = spawn_entry.amount_max - 1 + if spawn_entry.amount_max == 0 then + table.remove(valid_unit_types, i) + total_wgt = total_wgt - spawn_entry.freq + return true + end + end + end + + local i = 1 + while i <= #valid_unit_types do + local spawn_entry = valid_unit_types[i] + if i <= #valid_unit_types and wanted_nr_units > 0 and spawn_entry.amount_min and 0 < spawn_entry.amount_min and (not spawn_entry.amount_max or 0 < spawn_entry.amount_max) then + if not _add_unit_type_to_spawn_task(i, spawn_entry) then + i = i + 1 + end + else + i = i + 1 + end + end + while wanted_nr_units > 0 and #valid_unit_types ~= 0 do + local rand_wght = math.random() * total_wgt + local rand_i = 1 + local rand_entry + while true do + rand_entry = valid_unit_types[rand_i] + rand_wght = rand_wght - rand_entry.freq + if rand_wght <= 0 then + break + else + rand_i = rand_i + 1 + end + end + local cat_data = unit_categories[rand_entry.unit] + if cat_data.special_type and tweak_data.group_ai.special_unit_spawn_limits[cat_data.special_type] and _get_special_unit_type_count(cat_data.special_type) >= tweak_data.group_ai.special_unit_spawn_limits[cat_data.special_type] then + table.remove(valid_unit_types, rand_i) + total_wgt = total_wgt - rand_entry.freq + else + _add_unit_type_to_spawn_task(rand_i, rand_entry) + end + end + local group_desc = {type = spawn_group_type, size = 0} + for u_name, spawn_info in pairs(spawn_task.units_remaining) do + group_desc.size = group_desc.size + spawn_info.amount + end + local group = self:_create_group(group_desc) + group.objective = grp_objective + group.objective.moving_out = true + group.team = self._teams[spawn_group.team_id or tweak_data.levels:get_default_team_ID("combatant")] + spawn_task.group = group + return group +end +]] diff --git a/GoonMod/lua/GroupAITweakData.lua b/GoonMod/lua/GroupAITweakData.lua new file mode 100644 index 0000000..efc99d7 --- /dev/null +++ b/GoonMod/lua/GroupAITweakData.lua @@ -0,0 +1,20 @@ + +CloneClass( GroupAITweakData ) + +Hooks:RegisterHook( "GroupAITweakDataPostInitTaskData" ) +function GroupAITweakData._init_task_data(self, difficulty_index, difficulty) + self.orig._init_task_data(self, difficulty_index, difficulty) + Hooks:Call( "GroupAITweakDataPostInitTaskData", self, difficulty_index, difficulty ) +end + +Hooks:RegisterHook( "GroupAITweakDataPostInitUnitCategories" ) +function GroupAITweakData._init_unit_categories(self, difficulty_index) + self.orig._init_unit_categories(self, difficulty_index) + Hooks:Call( "GroupAITweakDataPostInitUnitCategories", self, difficulty_index ) +end + +Hooks:RegisterHook( "GroupAITweakDataPostInitEnemySpawnGroups" ) +function GroupAITweakData._init_enemy_spawn_groups(self, difficulty_index) + self.orig._init_enemy_spawn_groups(self, difficulty_index) + Hooks:Call( "GroupAITweakDataPostInitEnemySpawnGroups", self, difficulty_index ) +end diff --git a/GoonMod/lua/HUDManager.lua b/GoonMod/lua/HUDManager.lua new file mode 100644 index 0000000..ba59320 --- /dev/null +++ b/GoonMod/lua/HUDManager.lua @@ -0,0 +1,35 @@ + +CloneClass( HUDManager ) + +Hooks:RegisterHook("HUDManagerSetStaminaValue") +function HUDManager.set_stamina_value(this, value) + this.orig.set_stamina_value(this, value) + Hooks:PCall("HUDManagerSetStaminaValue", this, value) +end + +Hooks:RegisterHook("HUDManagerSetMaxStamina") +function HUDManager.set_max_stamina(this, value) + this.orig.set_max_stamina(this, value) + Hooks:PCall("HUDManagerSetMaxStamina", this, value) +end + +Hooks:RegisterHook("HUDManagerSetMugshotDowned") +function HUDManager.set_mugshot_downed(this, id) + this.orig.set_mugshot_downed(this, id) + Hooks:PCall("HUDManagerSetMugshotDowned", this, id) +end + +Hooks:RegisterHook("HUDManagerPreAddWaypoint") +function HUDManager.add_waypoint(self, id, data) + local r = Hooks:ReturnCall("HUDManagerPreAddWaypoint", self, id, data) + if r then + return + end + return self.orig.add_waypoint(self, id, data) +end + +Hooks:RegisterHook("HUDManagerPreAddNameLabel") +function HUDManager._add_name_label(self, data) + Hooks:Call("HUDManagerPreAddNameLabel", self, data) + return self.orig._add_name_label(self, data) +end diff --git a/GoonMod/lua/HUDManagerPD2.lua b/GoonMod/lua/HUDManagerPD2.lua new file mode 100644 index 0000000..31b1b6a --- /dev/null +++ b/GoonMod/lua/HUDManagerPD2.lua @@ -0,0 +1,151 @@ + +CloneClass( HUDManager ) + +function HUDManager._create_teammates_panel(self, hud) + Print("HUDManager._create_teammates_panel(self, hud)") + self.orig._create_teammates_panel(self, hud) +end + +function HUDManager.remove_teammate_panel_by_name_id(self, name_id) + Print("HUDManager:remove_teammate_panel_by_name_id(" .. name_id .. ")") + self.orig.remove_teammate_panel_by_name_id(self, name_id) +end + +function HUDManager.remove_teammate_panel(self, id) + Print("HUDManager:remove_teammate_panel(" .. tostring(id) .. ")") + self.orig.remove_teammate_panel(self, id) +end + +function HUDManager:add_mugshot_by_unit(unit) + if unit:base().is_local_player then + return + end + local character_name = unit:base():nick_name() + local name_label_id = managers.hud:_add_name_label({name = character_name, unit = unit}) + unit:unit_data().name_label_id = name_label_id + local is_husk_player = unit:base().is_husk_player + local character_name_id = managers.criminals:character_name_by_unit(unit) + for i, data in ipairs(self._hud.mugshots) do + if data.character_name_id == character_name_id then + if is_husk_player and not data.peer_id then + -- self:_remove_mugshot(data.id) + break + else + unit:unit_data().mugshot_id = data.id + managers.hud:set_mugshot_normal(unit:unit_data().mugshot_id) + managers.hud:set_mugshot_armor(unit:unit_data().mugshot_id, 1) + managers.hud:set_mugshot_health(unit:unit_data().mugshot_id, 1) + return + end + end + end + local peer, peer_id + if is_husk_player then + peer = unit:network():peer() + peer_id = peer:id() + end + local use_lifebar = is_husk_player and true or false + local mugshot_id = managers.hud:add_mugshot({ + name = utf8.to_upper(character_name), + use_lifebar = use_lifebar, + peer_id = peer_id, + character_name_id = character_name_id + }) + unit:unit_data().mugshot_id = mugshot_id + if peer and peer:is_cheater() then + self:mark_cheater(peer_id) + end + return mugshot_id +end + +function HUDManager.add_teammate_panel(self, character_name, player_name, ai, peer_id) + + Print("HUDManager.add_teammate_panel (" .. tostring(character_name) .. " / " .. tostring(player_name) .. ")") + + for i, data in ipairs(self._hud.teammate_panels_data) do + Print(tostring(i) .. " / taken: " .. tostring(data.taken)) + if not data.taken then + + Print(tostring(i) .. " is not taken yet") + + self._teammate_panels[i]:add_panel() + self._teammate_panels[i]:set_peer_id(peer_id) + self._teammate_panels[i]:set_ai(ai) + self:set_teammate_callsign(i, ai and 5 or peer_id) + self:set_teammate_name(i, player_name) + self:set_teammate_state(i, ai and "ai" or "player") + + if peer_id then + + local peer_equipment = managers.player:get_synced_equipment_possession(peer_id) or {} + for equipment, amount in pairs(peer_equipment) do + self:add_teammate_special_equipment(i, { + id = equipment, + icon = tweak_data.equipments.specials[equipment].icon, + amount = amount + }) + end + + local peer_deployable_equipment = managers.player:get_synced_deployable_equipment(peer_id) + + if peer_deployable_equipment then + local icon = tweak_data.equipments[peer_deployable_equipment.deployable].icon + self:set_deployable_equipment(i, { + icon = icon, + amount = peer_deployable_equipment.amount + }) + end + + local peer_cable_ties = managers.player:get_synced_cable_ties(peer_id) + if peer_cable_ties then + local icon = tweak_data.equipments.specials.cable_tie.icon + self:set_cable_tie(i, { + icon = icon, + amount = peer_cable_ties.amount + }) + end + + local peer_grenades = managers.player:get_synced_grenades(peer_id) + if peer_grenades then + local icon = tweak_data.blackmarket.grenades[peer_grenades.grenade].icon + self:set_teammate_grenades(i, { + icon = icon, + amount = Application:digest_value(peer_grenades.amount, false) + }) + end + + end + + local unit = managers.player:player_unit(peer_id) + --local unit = managers.criminals:character_unit_by_name(character_name) + if alive(unit) then + local weapon = unit:inventory():equipped_unit() + if alive(weapon) then + local icon = weapon:base():weapon_tweak_data().hud_icon + local equipped_selection = unit:inventory():equipped_selection() + self:_set_teammate_weapon_selected(i, equipped_selection, icon) + end + end + + local peer_ammo_info = managers.player:get_synced_ammo_info(peer_id) + if peer_ammo_info then + for selection_index, ammo_info in pairs(peer_ammo_info) do + self:set_teammate_ammo_amount(i, selection_index, unpack(ammo_info)) + end + end + + local peer_carry_data = managers.player:get_synced_carry(peer_id) + + if peer_carry_data then + self:set_teammate_carry_info(i, peer_carry_data.carry_id, managers.loot:get_real_value(peer_carry_data.carry_id, peer_carry_data.multiplier)) + end + + data.taken = true + + Print("id: " .. tostring(i)) + return i + + end + end + +end diff --git a/GoonMod/lua/InfamyTweakData.lua b/GoonMod/lua/InfamyTweakData.lua new file mode 100644 index 0000000..28faeec --- /dev/null +++ b/GoonMod/lua/InfamyTweakData.lua @@ -0,0 +1,6 @@ + +CloneClass( InfamyTweakData ) + +function InfamyTweakData.init(self) + self.orig.init(self) +end diff --git a/GoonMod/lua/InteractionExt.lua b/GoonMod/lua/InteractionExt.lua new file mode 100644 index 0000000..693b6ae --- /dev/null +++ b/GoonMod/lua/InteractionExt.lua @@ -0,0 +1,14 @@ + +CloneClass( BaseInteractionExt ) + +local ids_contour_color = Idstring("contour_color") +local ids_contour_opacity = Idstring("contour_opacity") +Hooks:RegisterHook("BaseInteractionExtPreSetContour") +function BaseInteractionExt.set_contour(self, color, opacity) + local r = Hooks:ReturnCall("BaseInteractionExtPreSetContour", self, color, opacity) + if r ~= nil then + color = r.color + opacity = r.opacity + end + self.orig.set_contour(self, color, opacity) +end diff --git a/GoonMod/lua/JobManager.lua b/GoonMod/lua/JobManager.lua new file mode 100644 index 0000000..7f68a70 --- /dev/null +++ b/GoonMod/lua/JobManager.lua @@ -0,0 +1,8 @@ + +CloneClass( JobManager ) + +Hooks:RegisterHook("JobManagerOnSetNextInteruptStage") +function JobManager.set_next_interupt_stage(self, interupt) + self.orig.set_next_interupt_stage(self, interupt) + Hooks:Call("JobManagerOnSetNextInteruptStage", self, interupt) +end diff --git a/GoonMod/lua/LevelsTweakData.lua b/GoonMod/lua/LevelsTweakData.lua new file mode 100644 index 0000000..23f67d8 --- /dev/null +++ b/GoonMod/lua/LevelsTweakData.lua @@ -0,0 +1,8 @@ + +CloneClass( LevelsTweakData ) + +Hooks:RegisterHook("LevelsTweakDataInit") +function LevelsTweakData.init(self) + self.orig.init(self) + Hooks:Call("LevelsTweakDataInit", self) +end diff --git a/GoonMod/lua/MaskExt.lua b/GoonMod/lua/MaskExt.lua new file mode 100644 index 0000000..6f5051d --- /dev/null +++ b/GoonMod/lua/MaskExt.lua @@ -0,0 +1,20 @@ + +CloneClass( MaskExt ) + +function MaskExt.clbk_texture_loaded(self, async_clbk, tex_name) + if not alive(self._unit) then + return + end + for tex_id, texture_data in pairs(self._textures) do + if not texture_data.ready and tex_name == texture_data.name then + texture_data.ready = true + local new_texture = TextureCache:retrieve(tex_name, "normal") + for _, material in ipairs(self._materials) do + material:set_texture(tex_id == "pattern" and "material_texture" or "reflection_texture", new_texture) + end + TextureCache:unretrieve(tex_name) + TextureCache:unretrieve(tex_name) + end + end + self:_chk_load_complete(async_clbk) +end diff --git a/GoonMod/lua/MenuComponentManager.lua b/GoonMod/lua/MenuComponentManager.lua new file mode 100644 index 0000000..a2c83cb --- /dev/null +++ b/GoonMod/lua/MenuComponentManager.lua @@ -0,0 +1,8 @@ + +CloneClass( MenuComponentManager ) + +Hooks:RegisterHook("PostCreateCrimenetContractGUI") +function MenuComponentManager._create_crimenet_contract_gui(self, node) + self.orig._create_crimenet_contract_gui(self, node) + Hooks:Call("PostCreateCrimenetContractGUI", self, node, self._crimenet_contract_gui) +end diff --git a/GoonMod/lua/MenuManager.lua b/GoonMod/lua/MenuManager.lua new file mode 100644 index 0000000..8a873dc --- /dev/null +++ b/GoonMod/lua/MenuManager.lua @@ -0,0 +1,83 @@ + +CloneClass( MenuManager ) +CloneClass( MenuCallbackHandler ) + +Hooks:RegisterHook( "MenuManagerSetMouseSensitivity" ) +function MenuManager.set_mouse_sensitivity(self, zoomed) + self.orig.set_mouse_sensitivity(self, zoomed) + Hooks:Call( "MenuManagerSetMouseSensitivity", self, zoomed ) +end + +-- Start game delaying +Hooks:RegisterHook("MenuCallbackHandlerPreStartTheGame") +function MenuCallbackHandler.start_the_game(self) + + if not self._start_the_game then + self._start_the_game = function() + self.orig.start_the_game() + end + end + + local r = Hooks:ReturnCall("MenuCallbackHandlerPreStartTheGame", self) + if r then + self._delayed_start_game = true + return nil + end + + self.orig.start_the_game(self) +end + +function MenuCallbackHandler:_process_start_game_delay( t, dt ) + + if self._delayed_start_game and #self._start_delays == 0 then + self._delayed_start_game = false + if self._start_the_game then + self:_start_the_game() + end + end + +end + +function MenuCallbackHandler:delay_game_start( id ) + + self._delayed_start_game = true + + if not self._start_delays then + self._start_delays = {} + end + table.insert( self._start_delays, id ) + +end + +function MenuCallbackHandler:release_game_start_delay( id ) + + for i = #self._start_delays, 0, -1 do + if self._start_delays[i] == id then + table.remove( self._start_delays, i ) + end + end + +end + +Hooks:Add("MenuUpdate", "MenuUpdate_MenuManager", function(t, dt) + MenuCallbackHandler._process_start_game_delay(MenuCallbackHandler, t, dt) +end) + +-- Lobby permissions +Hooks:RegisterHook("MenuCallbackHandlerPreChoseLobbyPermission") +function MenuCallbackHandler.choice_lobby_permission(self, item) + local r = Hooks:ReturnCall("MenuCallbackHandlerPreChoseLobbyPermission", self, item) + if r then + return + end + self.orig.choice_lobby_permission(self, item) +end + +Hooks:RegisterHook("MenuCallbackHandlerPreCrimeNetChoseLobbyPermission") +function MenuCallbackHandler.choice_crimenet_lobby_permission(self, item) + local r = Hooks:ReturnCall("MenuCallbackHandlerPreCrimeNetChoseLobbyPermission", self, item) + if r then + return + end + self.orig.choice_crimenet_lobby_permission(self, item) +end diff --git a/GoonMod/lua/MenuSceneManager.lua b/GoonMod/lua/MenuSceneManager.lua new file mode 100644 index 0000000..9c0f114 --- /dev/null +++ b/GoonMod/lua/MenuSceneManager.lua @@ -0,0 +1,18 @@ + +CloneClass( MenuSceneManager ) + +Hooks:RegisterHook("MenuSceneManagerSpawnedItemWeapon") +function MenuSceneManager.spawn_item_weapon(self, factory_id, blueprint, texture_switches) + local unit = self.orig.spawn_item_weapon(self, factory_id, blueprint, texture_switches) + Hooks:Call("MenuSceneManagerSpawnedItemWeapon", factory_id, blueprint, texture_switches, unit) + return unit +end + +Hooks:RegisterHook("MenuSceneManagerOverrideSceneTemplate") +function MenuSceneManager.set_scene_template(self, template, data, custom_name, skip_transition) + local r = Hooks:ReturnCall("MenuSceneManagerOverrideSceneTemplate", self, template, data, custom_name, skip_transition) + if r then + template = r + end + self.orig.set_scene_template(self, template, data, custom_name, skip_transition) +end diff --git a/GoonMod/lua/MenuSetup.lua b/GoonMod/lua/MenuSetup.lua new file mode 100644 index 0000000..f6b504a --- /dev/null +++ b/GoonMod/lua/MenuSetup.lua @@ -0,0 +1,14 @@ + +CloneClass( MenuSetup ) + +Hooks:RegisterHook("MenuUpdate") +function MenuSetup.update(self, t, dt) + self.orig.update(self, t, dt) + Hooks:Call("MenuUpdate", t, dt) +end + +Hooks:RegisterHook("SetupOnQuit") +function MenuSetup.quit(self) + Hooks:Call("SetupOnQuit", self) + return self.orig.quit(self) +end diff --git a/GoonMod/lua/MissionBriefingGUI.lua b/GoonMod/lua/MissionBriefingGUI.lua new file mode 100644 index 0000000..98e7997 --- /dev/null +++ b/GoonMod/lua/MissionBriefingGUI.lua @@ -0,0 +1,10 @@ + +CloneClass( MissionBriefingGui ) + +Hooks:RegisterHook("MissionBriefingGUIPreInit") +Hooks:RegisterHook("MissionBriefingGUIPostInit") +function MissionBriefingGui.init(self, saferect_ws, fullrect_ws, node) + Hooks:Call( "MissionBriefingGUIPreInit", self, saferect_ws, fullrect_ws, node ) + self.orig.init(self, saferect_ws, fullrect_ws, node) + Hooks:Call( "MissionBriefingGUIPostInit", self, saferect_ws, fullrect_ws, node ) +end diff --git a/GoonMod/lua/NPCRaycastWeaponBase.lua b/GoonMod/lua/NPCRaycastWeaponBase.lua new file mode 100644 index 0000000..aa7fd00 --- /dev/null +++ b/GoonMod/lua/NPCRaycastWeaponBase.lua @@ -0,0 +1,20 @@ + +CloneClass( NPCRaycastWeaponBase ) + +Hooks:RegisterHook("NPCRaycastWeaponBaseInit") +function NPCRaycastWeaponBase.init(self, unit) + self.orig.init(self, unit) + Hooks:Call("NPCRaycastWeaponBaseInit", self, unit) +end +--[[ dunno if these exist for enemy weapons but whatever +Hooks:RegisterHook("NPCRaycastWeaponBaseUpdate") +function NPCRaycastWeaponBase.update(self, unit, t, dt) + Hooks:Call("NPCRaycastWeaponBaseUpdate", self, unit, t, dt) +end + +Hooks:RegisterHook("NPCRaycastWeaponBaseSetFactoryData") +function NPCRaycastWeaponBase.set_factory_data(self, factory_id) + self.orig.set_factory_data(self, factory_id) + Hooks:Call("NPCRaycastWeaponBaseSetFactoryData", self, factory_id) +end +]]-- diff --git a/GoonMod/lua/NarrativeTweakData.lua b/GoonMod/lua/NarrativeTweakData.lua new file mode 100644 index 0000000..7b92612 --- /dev/null +++ b/GoonMod/lua/NarrativeTweakData.lua @@ -0,0 +1,8 @@ + +CloneClass( NarrativeTweakData ) + +Hooks:RegisterHook("NarrativeTweakDataInit") +function NarrativeTweakData.init(self) + self.orig.init(self) + Hooks:Call("NarrativeTweakDataInit", self) +end diff --git a/GoonMod/lua/NetworkGame.lua b/GoonMod/lua/NetworkGame.lua new file mode 100644 index 0000000..6d88cc1 --- /dev/null +++ b/GoonMod/lua/NetworkGame.lua @@ -0,0 +1,21 @@ + +CloneClass( NetworkGame ) + +Hooks:RegisterHook("NetworkGamePostLoad") +function NetworkGame.load(self, game_data) + Print("NetworkGame.load()") + self.orig.load(self, game_data) + Hooks:Call("NetworkGamePostLoad", self, game_data) +end + +function NetworkGame.on_network_started(self) + Print("NetworkGame.on_network_started()") + self.orig.on_network_started(self) +end + + +function NetworkGame.init(self) + Print("NetworkGame.init()") + self.orig.init(self) + Print("network manager: ", managers.network) +end diff --git a/GoonMod/lua/NetworkMatchMakingSteam.lua b/GoonMod/lua/NetworkMatchMakingSteam.lua new file mode 100644 index 0000000..7972cbc --- /dev/null +++ b/GoonMod/lua/NetworkMatchMakingSteam.lua @@ -0,0 +1,17 @@ + +CloneClass( NetworkMatchMakingSTEAM ) + +Hooks:RegisterHook("NetworkMatchmakingSetAttributes") +function NetworkMatchMakingSTEAM.set_attributes(self, settings) + self.orig.set_attributes(self, settings) + Hooks:Call("NetworkMatchmakingSetAttributes", self, settings) + if self.lobby_handler then + self.lobby_handler:set_lobby_data( self._lobby_attributes ) + end +end + +Hooks:RegisterHook("NetworkMatchmakingJoinOKServer") +function NetworkMatchMakingSTEAM.join_server(self, room_id, skip_showing_dialog) + Hooks:Call("NetworkMatchmakingJoinOKServer", self, room_id, skip_showing_dialog) + self.orig.join_server(self, room_id, skip_showing_dialog) +end diff --git a/GoonMod/lua/NewRaycastWeaponBase.lua b/GoonMod/lua/NewRaycastWeaponBase.lua new file mode 100644 index 0000000..396ae8e --- /dev/null +++ b/GoonMod/lua/NewRaycastWeaponBase.lua @@ -0,0 +1,25 @@ + +CloneClass( NewRaycastWeaponBase ) + +Hooks:RegisterHook("NewRaycastWeaponBaseInit") +function NewRaycastWeaponBase.init(self, unit) + self.orig.init(self, unit) + Hooks:Call("NewRaycastWeaponBaseInit", self, unit) +end + +Hooks:RegisterHook("NewRaycastWeaponBaseUpdate") +function NewRaycastWeaponBase.update(self, unit, t, dt) + Hooks:Call("NewRaycastWeaponBaseUpdate", self, unit, t, dt) +end + +Hooks:RegisterHook("NewRaycastWeaponBaseSetFactoryData") +function NewRaycastWeaponBase.set_factory_data(self, factory_id) + self.orig.set_factory_data(self, factory_id) + Hooks:Call("NewRaycastWeaponBaseSetFactoryData", self, factory_id) +end + +Hooks:RegisterHook("NewRaycastWeaponBasePostAssemblyComplete") +function NewRaycastWeaponBase.clbk_assembly_complete(self, clbk, parts, blueprint) + self.orig.clbk_assembly_complete(self, clbk, parts, blueprint) + Hooks:Call("NewRaycastWeaponBasePostAssemblyComplete", self, clbk, parts, blueprint) +end diff --git a/GoonMod/lua/PlayerDamage.lua b/GoonMod/lua/PlayerDamage.lua new file mode 100644 index 0000000..5f16f62 --- /dev/null +++ b/GoonMod/lua/PlayerDamage.lua @@ -0,0 +1,20 @@ + +CloneClass( PlayerDamage ) + +Hooks:RegisterHook( "PlayerDamageOnPostInit" ) +function PlayerDamage.init(this, unit) + this.orig.init(this, unit) + Hooks:Call("PlayerDamageOnPostInit", this, unit) +end + +Hooks:RegisterHook( "PlayerDamageOnRegenerated" ) +function PlayerDamage._regenerated(this, no_messiah) + this.orig._regenerated(this, no_messiah) + Hooks:Call("PlayerDamageOnRegenerated", this, no_messiah) +end + +Hooks:RegisterHook( "PlayerDamageOnDowned" ) +function PlayerDamage.on_downed(self) + self.orig.on_downed(self) + Hooks:Call("PlayerDamageOnDowned", self) +end diff --git a/GoonMod/lua/PlayerInventory.lua b/GoonMod/lua/PlayerInventory.lua new file mode 100644 index 0000000..8c0efd7 --- /dev/null +++ b/GoonMod/lua/PlayerInventory.lua @@ -0,0 +1,40 @@ + +CloneClass( PlayerInventory ) + +function PlayerInventory.add_unit_by_factory_name(self, factory_name, equip, instant, blueprint, texture_switches) + self.orig.add_unit_by_factory_name(self, factory_name, equip, instant, blueprint, texture_switches) +end + +function PlayerInventory._place_selection(self, selection_index, is_equip) + self.orig._place_selection(self, selection_index, is_equip) + self:create_riot_shield(self._unit) +end + +function PlayerInventory.create_riot_shield(self, unit) + + local psuccess, perror = pcall(function() + + -- local parent_unit = self._unit:camera()._camera_unit + -- local align_name = Idstring("a_weapon_right") + -- local align_obj = parent_unit:get_object( Idstring("a_weapon_right") ) + + -- self._shield_unit = World:spawn_unit(Idstring("units/payday2/characters/ene_acc_shield_lights/ene_acc_shield_lights"), align_obj:position(), align_obj:rotation()) + -- self._shield_unit:set_enabled(true) + -- self._shield_unit:damage():run_sequence_simple("held_body") + -- parent_unit:link(align_name, self._shield_unit, self._shield_unit:orientation_object():name()) + + -- local body = self._unit:body("mover_blocker") + -- if body then + -- body:set_enabled(false) + -- else + -- Print("could not find body") + -- end + + end) + if not psuccess then + Print("[Error] " .. perror) + end + +end + + diff --git a/GoonMod/lua/PlayerManager.lua b/GoonMod/lua/PlayerManager.lua new file mode 100644 index 0000000..39e82d1 --- /dev/null +++ b/GoonMod/lua/PlayerManager.lua @@ -0,0 +1,17 @@ + +CloneClass( PlayerManager ) + +function PlayerManager.verify_carry(self, peer_id, carry_id) + if self._force_verify_carry and self._force_verify_carry > 0 then + self._force_verify_carry = self._force_verify_carry - 1 + return true + end + return self.orig.verify_carry(self, peer_id, carry_id) +end + +function PlayerManager:force_verify_carry() + if not self._force_verify_carry then + self._force_verify_carry = 0 + end + self._force_verify_carry = self._force_verify_carry + 1 +end diff --git a/GoonMod/lua/PlayerStandard.lua b/GoonMod/lua/PlayerStandard.lua new file mode 100644 index 0000000..aa9c27c --- /dev/null +++ b/GoonMod/lua/PlayerStandard.lua @@ -0,0 +1,38 @@ + +CloneClass( PlayerStandard ) + +Hooks:RegisterHook("PlayerStandardCheckActionInteract") +function PlayerStandard._check_action_interact(self, t, input) + local r = Hooks:ReturnCall("PlayerStandardCheckActionInteract", self, t, input) + if r ~= nil then + return r + end + return self.orig._check_action_interact(self, t, input) +end + +Hooks:RegisterHook("PlayerStandardStartMaskUp") +function PlayerStandard._enter(self, enter_data) + self.orig._enter(self, enter_data) + Hooks:Call("PlayerStandardStartMaskUp", self, enter_data) +end + +Hooks:RegisterHook("PlayerStandardStartActionEquipWeapon") +function PlayerStandard._start_action_equip_weapon(self, t) + self.orig._start_action_equip_weapon(self, t) + Hooks:Call("PlayerStandardStartActionEquipWeapon", self, t) +end + +Hooks:RegisterHook("PlayerStandardChangingWeapon") +function PlayerStandard._changing_weapon(self) + Hooks:Call("PlayerStandardChangingWeapon", self) + return self.orig._changing_weapon(self) +end + +Hooks:RegisterHook("PlayerStandardCheckActionThrowGrenade") +function PlayerStandard._check_action_throw_grenade(self, t, input) + local r = Hooks:ReturnCall("PlayerStandardCheckActionThrowGrenade", self, t, input) + if r ~= nil then + return r + end + return self.orig._check_action_throw_grenade(self, t, input) +end diff --git a/GoonMod/lua/PreplanningTweakData.lua b/GoonMod/lua/PreplanningTweakData.lua new file mode 100644 index 0000000..479de61 --- /dev/null +++ b/GoonMod/lua/PreplanningTweakData.lua @@ -0,0 +1,35 @@ + +CloneClass( PrePlanningTweakData ) + +function PrePlanningTweakData._create_locations(this, tweak_data) + + this.orig._create_locations(this, tweak_data) + + this.locations.branchbank = { + default_plans = { + escape_plan = "escape_helicopter_loud", + vault_plan = "vault_big_drill" + }, + total_budget = 10, + start_location = { + group = "a", + x = 1500, + y = 1025, + zoom = 1.5 + }, + { + name_id = "menu_pp_big_loc_a", + texture = "guis/textures/pd2/mission_briefing/assets/bank/assets_bank_blueprint", + map_x = -1.1, + map_y = 0.5, + map_size = 1, + x1 = -250, + y1 = -3000, + x2 = 5750, + y2 = 3000, + rotation = 0, + custom_points = {} + } + } + +end diff --git a/GoonMod/lua/QuickSmokeGrenade.lua b/GoonMod/lua/QuickSmokeGrenade.lua new file mode 100644 index 0000000..34ef0ae --- /dev/null +++ b/GoonMod/lua/QuickSmokeGrenade.lua @@ -0,0 +1,14 @@ + +CloneClass( QuickSmokeGrenade ) + +Hooks:RegisterHook( "QuickSmokeGrenadeActivate" ) +function QuickSmokeGrenade.activate(this, pos, duration) + this.orig.activate(this, pos, duration) + Hooks:Call("QuickSmokeGrenadeActivate", this, pos, duration) +end + +Hooks:RegisterHook( "QuickSmokeGrenadeDetonate" ) +function QuickSmokeGrenade.detonate(this) + this.orig.detonate(this) + Hooks:Call("QuickSmokeGrenadeDetonate", this) +end diff --git a/GoonMod/lua/Setup.lua b/GoonMod/lua/Setup.lua new file mode 100644 index 0000000..38d55a2 --- /dev/null +++ b/GoonMod/lua/Setup.lua @@ -0,0 +1,8 @@ + +CloneClass( Setup ) + +Hooks:RegisterHook("SetupOnQuit") +function Setup.quit(self) + Hooks:Call("SetupOnQuit", self) + return self.orig.quit(self) +end diff --git a/GoonMod/lua/SpoocLogicAttack.lua b/GoonMod/lua/SpoocLogicAttack.lua new file mode 100644 index 0000000..1c0e95a --- /dev/null +++ b/GoonMod/lua/SpoocLogicAttack.lua @@ -0,0 +1,42 @@ + + +function SpoocLogicAttack.action_complete_clbk(data, action) + local action_type = action:type() + local my_data = data.internal_data + if action_type == "walk" then + my_data.advancing = nil + if my_data.surprised then + my_data.surprised = false + elseif my_data.moving_to_cover then + if action:expired() then + my_data.in_cover = my_data.moving_to_cover + CopLogicAttack._set_nearest_cover(my_data, my_data.in_cover) + my_data.cover_enter_t = data.t + my_data.cover_sideways_chk = nil + end + my_data.moving_to_cover = nil + elseif my_data.walking_to_cover_shoot_pos then + my_data.walking_to_cover_shoot_pos = nil + end + elseif action_type == "shoot" then + my_data.shooting = nil + elseif action_type == "turn" then + my_data.turning = nil + elseif action_type == "spooc" then + Print("SPOOKED BY A SPOOKY SPOOK") + data.spooc_attack_timeout_t = TimerManager:game():time() + math.lerp(data.char_tweak.spooc_attack_timeout[1], data.char_tweak.spooc_attack_timeout[2], math.random()) + if action:complete() and data.char_tweak.spooc_attack_use_smoke_chance > 0 and math.random() <= data.char_tweak.spooc_attack_use_smoke_chance and not managers.groupai:state():is_smoke_grenade_active() then + managers.groupai:state():detonate_smoke_grenade(data.m_pos + math.UP * 10, data.unit:movement():m_head_pos(), math.lerp(15, 30, math.random()), false) + end + my_data.spooc_attack = nil + elseif action_type == "dodge" then + local timeout = action:timeout() + if timeout then + data.dodge_timeout_t = TimerManager:game():time() + math.lerp(timeout[1], timeout[2], math.random()) + end + CopLogicAttack._cancel_cover_pathing(data, my_data) + if action:expired() then + SpoocLogicAttack._upd_aim(data, my_data) + end + end +end diff --git a/GoonMod/lua/TweakData.lua b/GoonMod/lua/TweakData.lua new file mode 100644 index 0000000..a1ac5d2 --- /dev/null +++ b/GoonMod/lua/TweakData.lua @@ -0,0 +1,9 @@ + +TweakData.orig = {} +TweakData.orig.init = TweakData.init + +Hooks:RegisterHook("TweakDataPostInit") +function TweakData.init(this) + this.orig.init(this) + Hooks:Call("TweakDataPostInit") +end diff --git a/GoonMod/lua/WeaponFlashlight.lua b/GoonMod/lua/WeaponFlashlight.lua new file mode 100644 index 0000000..9905269 --- /dev/null +++ b/GoonMod/lua/WeaponFlashlight.lua @@ -0,0 +1,46 @@ + +CloneClass( WeaponFlashLight ) + +Hooks:RegisterHook("WeaponFlashLightInit") +function WeaponFlashLight.init(self, unit) + self.orig.init(self, unit) + Hooks:Call( "WeaponFlashLightInit", self, unit ) +end + +Hooks:RegisterHook("WeaponFlashLightCheckState") +function WeaponFlashLight._check_state(self) + self.orig._check_state(self) + Hooks:Call( "WeaponFlashLightCheckState", self ) +end + +-- TODO: This is a messy hack-fix. Fix this up proper sometime. +function WeaponFlashLight:overkill_update(unit, t, dt) + + t = Application:time() + self._light_speed = self._light_speed or 1 + self._light_speed = math.step(self._light_speed, 1, dt * (math.random(4) + 2)) + -- self._light:set_rotation(self._light:rotation() * Rotation(dt * -50 * self._light_speed, 0)) + self:update_flicker(t, dt) + self:update_laughter(t, dt) + if not self._kittens_timer then + self._kittens_timer = t + 25 + end + if t > self._kittens_timer then + if math.rand(1) < 0.75 then + self:run_net_event(self.HALLOWEEN_FLICKER) + self._kittens_timer = t + math.random(10) + 5 + elseif math.rand(1) < 0.35 then + self:run_net_event(self.HALLOWEEN_WARP) + self._kittens_timer = t + math.random(12) + 3 + elseif math.rand(1) < 0.25 then + self:run_net_event(self.HALLOWEEN_LAUGHTER) + self._kittens_timer = t + math.random(5) + 8 + elseif math.rand(1) < 0.15 then + self:run_net_event(self.HALLOWEEN_SPOOC) + self._kittens_timer = t + math.random(2) + 3 + else + self._kittens_timer = t + math.random(5) + 3 + end + end + +end diff --git a/GoonMod/lua/WeaponLaser.lua b/GoonMod/lua/WeaponLaser.lua new file mode 100644 index 0000000..64c286a --- /dev/null +++ b/GoonMod/lua/WeaponLaser.lua @@ -0,0 +1,37 @@ + +CloneClass( WeaponLaser ) + +Hooks:RegisterHook("WeaponLaserInit") +function WeaponLaser.init(self, unit) + self.orig.init(self, unit) + Hooks:Call("WeaponLaserInit", self, unit) +end + +Hooks:RegisterHook("WeaponLaserUpdate") +function WeaponLaser.update(self, unit, t, dt) + self.orig.update(self, unit, t, dt) + Hooks:Call("WeaponLaserUpdate", self, unit, t, dt) +end + +Hooks:RegisterHook("WeaponLaserSetNPC") +function WeaponLaser.set_npc(self) + self.orig.set_npc(self) + Hooks:Call("WeaponLaserSetNPC", self) +end + +Hooks:RegisterHook("WeaponLaserPostSetColorByTheme") +function WeaponLaser.set_color_by_theme(self, theme) + self.orig.set_color_by_theme(self, theme) + Hooks:Call("WeaponLaserPostSetColorByTheme", self, theme) +end + +Hooks:RegisterHook("WeaponLaserSetOn") +Hooks:RegisterHook("WeaponLaserSetOff") +function WeaponLaser._check_state(self) + self.orig._check_state(self) + if self._on then + Hooks:Call("WeaponLaserSetOn", self) + else + Hooks:Call("WeaponLaserSetOff", self) + end +end diff --git a/GoonMod/lua/WeaponTweakData.lua b/GoonMod/lua/WeaponTweakData.lua new file mode 100644 index 0000000..cd0d723 --- /dev/null +++ b/GoonMod/lua/WeaponTweakData.lua @@ -0,0 +1,8 @@ + +CloneClass( WeaponTweakData ) + +Hooks:RegisterHook("WeaponTweakDataInitNewWeapons") +function WeaponTweakData._init_new_weapons(self, ...) + self.orig._init_new_weapons(self, arg) + Hooks:Call("WeaponTweakDataInitNewWeapons") +end diff --git a/GoonMod/menus/corpse_mod_menu.txt b/GoonMod/menus/corpse_mod_menu.txt new file mode 100644 index 0000000..68ff0c9 --- /dev/null +++ b/GoonMod/menus/corpse_mod_menu.txt @@ -0,0 +1,79 @@ +{ + "menu_id" : "gm_options_corpse_menu", + "parent_menu_id" : "goonbase_options_menu", + "title" : "gm_options_corpse_menu_title", + "description" : "gm_options_corpse_menu_desc", + "items" : [ + + { + "type" : "toggle", + "id" : "gm_cm_toggle_corpse_limit", + "title" : "gm_options_corpse_custom_title", + "description" : "gm_options_corpse_custom_desc", + "callback" : "ToggleCorpseLimit", + "value" : "UseCustomCorpseLimit", + "default_value" : true, + }, + { + "type" : "slider", + "id" : "gm_cm_slider_corpse_limit", + "title" : "gm_options_corpse_amount_title", + "description" : "gm_options_corpse_amount_desc", + "callback" : "SetMaximumCorpseAmount", + "value" : "MaxCorpses", + "default_value" : 256, + "min" : 8, + "max" : 1024, + "step" : 8, + }, + { + "type" : "divider", + "size" : 16, + }, + + { + "type" : "toggle", + "id" : "gm_cm_toggle_shield_despawn", + "title" : "gm_options_corpse_shields_title", + "description" : "gm_options_corpse_shields_desc", + "callback" : "ToggleDespawnShields", + "value" : "RemoveShields", + "default_value" : false, + }, + { + "type" : "slider", + "id" : "gm_cm_slider_shield_despawn_time", + "title" : "gm_options_corpse_shields_timer_title", + "description" : "gm_options_corpse_shields_timer_desc", + "callback" : "SetShieldDespawnTime", + "value" : "RemoveShieldsTime", + "default_value" : 180, + "min" : 10, + "max" : 600, + "step" : 10, + }, + { + "type" : "divider", + "size" : 16, + }, + + { + "type" : "keybind", + "id" : "gm_cm_keybind_remove_all", + "title" : "gm_options_corpse_keybind_remove_all_title", + "description" : "gm_options_corpse_keybind_remove_all_desc", + "keybind_id" : "BodyCountModRemoveAll", + "func" : "DoRemoveAllCorpses", + }, + { + "type" : "keybind", + "id" : "gm_cm_keybind_remove_shields", + "title" : "gm_options_corpse_keybind_remove_shields_title", + "description" : "gm_options_corpse_keybind_remove_shields_desc", + "keybind_id" : "BodyCountModRemoveShields", + "func" : "DoRemoveAllShields", + } + + ] + +} \ No newline at end of file diff --git a/GoonMod/menus/example_menu.txt b/GoonMod/menus/example_menu.txt new file mode 100644 index 0000000..1b80ea3 --- /dev/null +++ b/GoonMod/menus/example_menu.txt @@ -0,0 +1,68 @@ +{ + "menu_id" : "json_example_menu", + "parent_menu_id" : "lua_mod_options_menu", + "title" : "my_custom_menu", + "description" : "my_custom_menu_desc", + "items" : [ + + { + "type" : "toggle", + "id" : "json_menu_toggle", + "title" : "json_item_toggle", + "description" : "json_item_toggle_desc", + "callback" : "callback_test_toggle", + "value" : "toggle_value", + "default_value" : false, + }, + { + "type" : "slider", + "id" : "json_menu_slider", + "title" : "json_item_slider", + "description" : "json_item_slider_desc", + "callback" : "callback_test_slider", + "value" : "slider_value", + "default_value" : 50, + "max" : 100, + "min" : 0, + "step" : 1, + }, + { + "type" : "divider", + "size" : 128, + }, + { + "type" : "button", + "id" : "json_menu_button", + "title" : "json_item_button", + "description" : "json_item_button_desc", + "callback" : "callback_test_button", + "back_callback" : "callback_test_button_back", + }, + { + "type" : "keybind", + "id" : "json_menu_keybind", + "title" : "json_item_keybind", + "description" : "json_item_keybind_desc", + "keybind_id" : "json_menu_example_keybind", + "func" : "func_test_keybind", + }, + { + "type" : "multiple_choice", + "id" : "json_menu_mutli", + "title" : "json_item_multi", + "description" : "json_item_multi_desc", + "callback" : "callback_test_multi", + "items" : [ + "json_multi_item_a", + "json_multi_item_b", + "json_multi_item_c", + "json_multi_item_d", + "json_multi_item_e" + ], + "value" : "multi_value", + "default_value" : 3, + } + + ] + +} \ No newline at end of file diff --git a/GoonMod/mod.txt b/GoonMod/mod.txt new file mode 100644 index 0000000..12750de --- /dev/null +++ b/GoonMod/mod.txt @@ -0,0 +1,67 @@ +{ + "name" : "GoonMod", + "author" : "James Wilkinson", + "contact" : "jw@jameswilko.com", + "updates" : [ + { + "revision" : 11, + "identifier" : "goonmod", + }, + { + "revision" : 0, + "identifier" : "goonmodwepcust", + "install_dir" : "assets/mod_overrides/", + "install_folder" : "GoonModWeaponCustomizer", + "display_name" : "GoonMod Weapon Customizer" + } + ], + "hooks" : [ + { "hook_id" : "lib/managers/localizationmanager", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/managers/menumanager", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/managers/chatmanager", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/managers/enemymanager", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/units/weapons/grenades/quicksmokegrenade", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/managers/hudmanager", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/managers/jobmanager", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/managers/groupaimanager", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/managers/group_ai_states/groupaistatebase", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/managers/group_ai_states/groupaistatebesiege", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/units/beings/player/states/playerstandard", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/units/beings/player/playerdamage", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/managers/playermanager", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/managers/gageassignmentmanager", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/managers/achievmentmanager", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/tweak_data/infamytweakdata", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/setups/gamesetup", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/setups/menusetup", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/managers/menu/blackmarketgui", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/managers/blackmarketmanager", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/tweak_data/groupaitweakdata", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/tweak_data/charactertweakdata", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/units/enemies/cop/copinventory", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/units/enemies/cop/copdamage", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/managers/mission/elementlasertrigger", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/units/weapons/weaponflashlight", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/units/weapons/weaponlaser", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/tweak_data/levelstweakdata", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/tweak_data/assetstweakdata", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/tweak_data/narrativetweakdata", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/managers/menu/menunodegui", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/managers/menu/items/menuitemcustomizecontroller", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/managers/criminalsmanager", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/units/weapons/newraycastweaponbase", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/units/weapons/npcraycastweaponbase", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/units/cameras/fpcameraplayerbase", "script_path" : "goonbase.lua" }, + { "hook_id" : "core/lib/managers/menu/items/coremenuitemslider", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/utils/game_state_machine/gamestatemachine", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/units/contourext", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/units/interactions/interactionext", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/units/enemies/spooc/actions/lower_body/actionspooc", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/managers/menu/menucomponentmanager", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/managers/menu/missionbriefinggui", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/network/matchmaking/networkmatchmakingsteam", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/managers/menu/menuscenemanager", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/units/enemies/cop/actions/full_body/copactionhurt", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/units/equipment/ecm_jammer/ecmjammerbase", "script_path" : "goonbase.lua" } + ] +} diff --git a/GoonMod/mods/body_count.lua b/GoonMod/mods/body_count.lua new file mode 100644 index 0000000..ec0c76e --- /dev/null +++ b/GoonMod/mods/body_count.lua @@ -0,0 +1,95 @@ + +-- Mod Definition +local Mod = class( BaseMod ) +Mod.id = "BodyCountMod" +Mod.Name = "Corpse Delimiter" +Mod.Desc = "Change the amount of bodies that can appear after enemies are killed." +Mod.Requirements = {} +Mod.Incompatibilities = {} +Mod.EnabledByDefault = true + +Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod:ID(), function() + GoonBase.Mods:RegisterMod( Mod ) +end) + +if not Mod:IsEnabled() then + return +end + +-- Body Count Mod +_G.GoonBase.CorpseDelimiter = _G.GoonBase.CorpseDelimiter or {} + +-- Options +GoonBase.Options.BodyCount = GoonBase.Options.BodyCount or {} +GoonBase.Options.BodyCount.UseCustomCorpseLimit = GoonBase.Options.BodyCount.UseCustomCorpseLimit or true +GoonBase.Options.BodyCount.MaxCorpses = GoonBase.Options.BodyCount.MaxCorpses or 256 +GoonBase.Options.BodyCount.RemoveShields = GoonBase.Options.BodyCount.RemoveShields or false +GoonBase.Options.BodyCount.RemoveShieldsTime = GoonBase.Options.BodyCount.RemoveShieldsTime or 120 + +-- Stop bodies from despawning +Hooks:Add("EnemyManagerPreUpdateCorpseDisposal", "EnemyManagerPreUpdateCorpseDisposal_BodyCount", function(enemy_manager) + enemy_manager._MAX_NR_CORPSES = GoonBase.Options.BodyCount.UseCustomCorpseLimit and GoonBase.Options.BodyCount.MaxCorpses or 8 +end) + +-- Despawn shields after time +local shield_timer_id = 0 +Hooks:Add("CopInventoryDropShield", "CopInventoryDropShield_BodyCount", function(inventory) + + if GoonBase.Options.BodyCount.UseCustomCorpseLimit and GoonBase.Options.BodyCount.RemoveShields then + + local id = "CopInventoryDropShield_" .. tostring(shield_timer_id) + shield_timer_id = shield_timer_id + 1 + + Queue:Add(id, function() + if inventory ~= nil then + inventory:destroy_all_items() + end + end, GoonBase.Options.BodyCount.RemoveShieldsTime) + + end + +end) + +-- Menu +Hooks:Add( "MenuManagerInitialize", "MenuManagerInitialize_" .. Mod:ID(), function( menu_manager ) + + -- Callbacks + MenuCallbackHandler.ToggleCorpseLimit = function(this, item) + GoonBase.Options.BodyCount.UseCustomCorpseLimit = item:value() == "on" and true or false + GoonBase.Options:Save() + end + + MenuCallbackHandler.SetMaximumCorpseAmount = function(this, item) + GoonBase.Options.BodyCount.MaxCorpses = math.floor( item:value() ) + GoonBase.Options:Save() + end + + MenuCallbackHandler.ToggleDespawnShields = function(this, item) + GoonBase.Options.BodyCount.RemoveShields = item:value() == "on" and true or false + GoonBase.Options:Save() + end + + MenuCallbackHandler.SetShieldDespawnTime = function(this, item) + GoonBase.Options.BodyCount.RemoveShieldsTime = math.floor( item:value() ) + GoonBase.Options:Save() + end + + GoonBase.CorpseDelimiter.DoRemoveAllCorpses = function(self) + managers.enemy:dispose_all_corpses() + end + + GoonBase.CorpseDelimiter.DoRemoveAllShields = function(self) + + local enemy_data = managers.enemy._enemy_data + local corpses = enemy_data.corpses + for u_key, u_data in pairs(corpses) do + if u_data.unit:inventory() ~= nil then + u_data.unit:inventory():destroy_all_items() + end + end + + end + + MenuHelper:LoadFromJsonFile( GoonBase.MenusPath .. "corpse_mod_menu.txt", GoonBase.CorpseDelimiter, GoonBase.Options.BodyCount ) + +end) diff --git a/GoonMod/mods/disabled/colors/color_hsvrgb.lua b/GoonMod/mods/disabled/colors/color_hsvrgb.lua new file mode 100644 index 0000000..da7c004 --- /dev/null +++ b/GoonMod/mods/disabled/colors/color_hsvrgb.lua @@ -0,0 +1,216 @@ + +ColorHSVRGB = class() + +function ColorHSVRGB:init() + self._ID = "DefaultID" + self._menu_id = nil + self._using_hsv = false + self._priority = 0 + self._r = 0 + self._g = 0 + self._b = 0 +end + +function ColorHSVRGB:ID() + return self._ID +end + +function ColorHSVRGB:UsingHSV() + return self._using_hsv +end + +function ColorHSVRGB:GetColor(alpha) + + local r, g, b = 0, 0, 0 + if self:UsingHSV() then + r, g, b = self:ToRGB(self._r, self._g, self._b) + else + r = self._r + g = self._g + b = self._b + end + + return Color(alpha or 1, r, g, b) + +end + +function ColorHSVRGB:ToRGB(h, s, v) + + local r, g, b + + local i = math.floor(h * 6) + local f = h * 6 - i + local p = v * (1 - s) + local q = v * (1 - f * s) + local t = v * (1 - (1 - f) * s) + + local mod = i % 6 + if mod == 0 then + r = v + g = t + b = p + elseif mod == 1 then + r = q + g = v + b = p + elseif mod == 2 then + r = p + g = v + b = t + elseif mod == 3 then + r = p + g = q + b = v + elseif mod == 4 then + r = t + g = p + b = v + elseif mod == 5 then + r = v + g = p + b = q + end + + return r, g, b + +end + +function ColorHSVRGB:SetID(id) + self._ID = id +end + +function ColorHSVRGB:SetHSV(hsv) + self._using_hsv = hsv +end + +function ColorHSVRGB:SetOptionsTable(tbl) + + self.options_table = tbl + + self._r = GoonBase.Options[tbl].R + self._g = GoonBase.Options[tbl].G + self._b = GoonBase.Options[tbl].B + self._using_hsv = GoonBase.Options[tbl].HSV + +end + +function ColorHSVRGB:SetupLocalization() + + local id = self:ID() + local hsv = self:UsingHSV() + local loc = managers.localization._custom_localizations + + loc["Options_ToggleColorHSV_Title"] = "Use HSV" + loc["Options_ToggleColorHSV_Message"] = "Use HSV instead of RGB" + + loc["Options_" .. id .. "_RH_Title"] = hsv and "Hue/Red" or "Red/Hue" + loc["Options_" .. id .. "_GS_Title"] = hsv and "Saturation/Green" or "Green/Saturation" + loc["Options_" .. id .. "_BV_Title"] = hsv and "Value/Blue" or "Blue/Value" + loc["Options_" .. id .. "_Example"] = "Color Example" + + loc["Options_" .. id .. "_RH_Desc"] = string.gsub( "Control the {1} of the colour", "{1}", hsv and "Hue" or "Red" ) + loc["Options_" .. id .. "_GS_Desc"] = string.gsub( "Control the {1} of the colour", "{1}", hsv and "Saturation" or "Green" ) + loc["Options_" .. id .. "_BV_Desc"] = string.gsub( "Control the {1} of the colour", "{1}", hsv and "Value" or "Blue" ) + loc["Options_" .. id .. "_ExampleMessage"] = "Re-open this menu to update the color example" + + managers.localization._custom_localizations = loc + +end + +function ColorHSVRGB:SetPriority( prio ) + self._priority = prio +end + +function ColorHSVRGB:SetupMenu( menu_id ) + + local id = self:ID() + local r = GoonBase.Options[self.options_table].R + local g = GoonBase.Options[self.options_table].G + local b = GoonBase.Options[self.options_table].B + local hsv = GoonBase.Options[self.options_table].HSV + + self:SetupLocalization() + self._menu_id = menu_id + + MenuCallbackHandler["set_rh_" .. id] = function(this, item) + self._r = item:value() + GoonBase.Options[self.options_table].R = self._r + GoonBase.Options:Save() + end + + MenuCallbackHandler["set_gs_" .. id] = function(this, item) + self._g = item:value() + GoonBase.Options[self.options_table].G = self._g + GoonBase.Options:Save() + end + + MenuCallbackHandler["set_bv_" .. id] = function(this, item) + self._b = item:value() + GoonBase.Options[self.options_table].B = self._b + GoonBase.Options:Save() + end + + MenuCallbackHandler["toggle_hsv_" .. id] = function(this, item) + + local enabled = item:value() == "on" and true or false + self:SetHSV( enabled ) + GoonBase.Options[self.options_table].HSV = enabled + GoonBase.Options:Save() + + self:SetupLocalization() + + end + + MenuHelper:AddSlider({ + id = "slider_rh_" .. id, + title = "Options_" .. id .. "_RH_Title", + desc = "Options_" .. id .. "_RH_Desc", + callback = "set_rh_" .. id, + value = r, + min = 0, + max = 1, + step = 0.01, + show_value = true, + menu_id = menu_id, + priority = self._priority + 5, + }) + + MenuHelper:AddSlider({ + id = "slider_gs_" .. id, + title = "Options_" .. id .. "_GS_Title", + desc = "Options_" .. id .. "_GS_Desc", + callback = "set_gs_" .. id, + value = g, + min = 0, + max = 1, + step = 0.01, + show_value = true, + menu_id = menu_id, + priority = self._priority + 4, + }) + + MenuHelper:AddSlider({ + id = "slider_bv_" .. id, + title = "Options_" .. id .. "_BV_Title", + desc = "Options_" .. id .. "_BV_Desc", + callback = "set_bv_" .. id, + value = b, + min = 0, + max = 1, + step = 0.01, + show_value = true, + menu_id = menu_id, + priority = self._priority + 3, + }) + + MenuHelper:AddToggle({ + id = "toggle_hsv_" .. id, + title = "Options_ToggleColorHSV_Title", + desc = "Options_ToggleColorHSV_Message", + callback = "toggle_hsv_" .. id, + value = hsv, + menu_id = menu_id, + priority = self._priority, + }) + +end diff --git a/GoonMod/mods/disabled/colors/enemy_weapon_laser.lua b/GoonMod/mods/disabled/colors/enemy_weapon_laser.lua new file mode 100644 index 0000000..ac5ff78 --- /dev/null +++ b/GoonMod/mods/disabled/colors/enemy_weapon_laser.lua @@ -0,0 +1,236 @@ + +-- Mod Definition +local Mod = class( BaseMod ) +Mod.id = "CustomEnemyWeaponLaser" +Mod.Name = "Custom Enemy Laser Colour" +Mod.Desc = "Set a custom colour for lasers attached to enemy weapons" +Mod.Requirements = {} +Mod.Incompatibilities = {} +Mod.Priority = 1 + +Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() + GoonBase.Mods:RegisterMod( Mod ) +end) + +if not Mod:IsEnabled() then + return +end + +-- Lasers +GoonBase.EnemyLaser = GoonBase.EnemyLaser or {} +local Laser = GoonBase.EnemyLaser +Laser.MenuId = "goonbase_enemy_laser_menu" +Laser.Color = nil + +-- Options +if GoonBase.Options.EnemyLaser == nil then + GoonBase.Options.EnemyLaser = {} + GoonBase.Options.EnemyLaser.Enabled = false + GoonBase.Options.EnemyLaser.R = 1 + GoonBase.Options.EnemyLaser.G = 0.1 + GoonBase.Options.EnemyLaser.B = 0.1 + GoonBase.Options.EnemyLaser.HSV = false + GoonBase.Options.EnemyLaser.Rainbow = false + GoonBase.Options.EnemyLaser.RainbowSpeed = 10 +end + +-- Functions +function Laser:IsEnabled() + return GoonBase.Options.EnemyLaser.Enabled +end + +function Laser:IsRainbow() + return GoonBase.Options.EnemyLaser.Rainbow +end + +function Laser:GetColor(alpha) + if Laser.Color == nil then + Laser.Color = ColorHSVRGB:new() + Laser.Color:SetOptionsTable( "EnemyLaser" ) + end + return Laser.Color:GetColor( alpha ) or Color( alpha or 1, 1, 0, 0 ) +end + +function Laser:IsNPCPlayerUnitLaser( laser ) + + if not self._laser_units_lookup then + self._laser_units_lookup = {} + end + + local laser_key = nil + if laser._unit then + laser_key = laser._unit:key() + end + if laser_key and self._laser_units_lookup[laser_key] ~= nil then + return self._laser_units_lookup[laser_key] + end + + local criminals_manager = managers.criminals + if not criminals_manager then + return + end + + for id, data in pairs(criminals_manager._characters) do + if data.unit ~= nil and alive(data.unit) and data.name ~= criminals_manager:local_character_name() then + + if data.unit:inventory() and data.unit:inventory():equipped_unit() then + + local wep_base = data.unit:inventory():equipped_unit():base() + if wep_base then + + if wep_base._factory_id ~= nil and wep_base._blueprint ~= nil then + + local gadgets = managers.weapon_factory:get_parts_from_weapon_by_type_or_perk("gadget", wep_base._factory_id, wep_base._blueprint) + if gadgets then + local gadget + for _, i in ipairs(gadgets) do + + gadget = wep_base._parts[i] + gadget = gadget.unit:base() + + if gadget == laser then + if laser_key then + self._laser_units_lookup[laser_key] = true + end + return true + end + + end + end + + end + + end + + end + + end + end + + if laser_key then + self._laser_units_lookup[laser_key] = false + end + return false + +end + +-- Menu +Hooks:Add("MenuManagerSetupCustomMenus", "MenuManagerSetupCustomMenus_EnemyLaser", function(menu_manager, menu_nodes) + MenuHelper:NewMenu( Laser.MenuId ) +end) + +Hooks:Add("MenuManagerSetupGoonBaseMenu", "MenuManagerSetupGoonBaseMenu_EnemyLaser", function(menu_manager, menu_nodes) + + -- Submenu Button + MenuHelper:AddButton({ + id = "enemy_laser_button", + title = "Options_EnemyLaserName", + desc = "Options_EnemyLaserDesc", + next_node = Laser.MenuId, + menu_id = "goonbase_options_menu" + }) + + -- Enabled Toggle + MenuCallbackHandler.toggle_custom_enemy_laser_color = function(this, item) + GoonBase.Options.EnemyLaser.Enabled = item:value() == "on" and true or false + GoonBase.Options:Save() + end + + MenuHelper:AddToggle({ + id = "toggle_custom_enemy_laser_color", + title = "Options_EnemyLaserEnableTitle", + desc = "Options_EnemyLaserEnableDesc", + callback = "toggle_custom_enemy_laser_color", + value = GoonBase.Options.EnemyLaser.Enabled, + menu_id = Laser.MenuId, + priority = 11 + }) + + -- RGB/HSV Colour + Laser.Color = ColorHSVRGB:new() + Laser.Color:SetID( "enemy_laser" ) + Laser.Color:SetPriority( 5 ) + Laser.Color:SetOptionsTable( "EnemyLaser" ) + Laser.Color:SetupMenu( Laser.MenuId ) + + -- Rainbow Laser + MenuCallbackHandler.toggle_enemy_laser_rainbow = function(this, item) + GoonBase.Options.EnemyLaser.Rainbow = item:value() == "on" and true or false + GoonBase.Options:Save() + end + + MenuCallbackHandler.enemy_laser_rainbow_speed = function(this, item) + GoonBase.Options.EnemyLaser.RainbowSpeed = item:value() + GoonBase.Options:Save() + end + + MenuHelper:AddToggle({ + id = "toggle_enemy_laser_rainbow", + title = "Options_EnemyLaserRainbowTitle", + desc = "Options_EnemyLaserRainbowDesc", + callback = "toggle_enemy_laser_rainbow", + value = GoonBase.Options.EnemyLaser.Rainbow, + menu_id = Laser.MenuId, + priority = 2 + }) + + MenuHelper:AddSlider({ + id = "enemy_laser_rainbow_speed", + title = "Options_EnemyLaserRainbowSpeedTitle", + desc = "Options_EnemyLaserRainbowSpeedDesc", + callback = "enemy_laser_rainbow_speed", + value = GoonBase.Options.EnemyLaser.RainbowSpeed, + min = 1, + max = 100, + step = 1, + show_value = true, + menu_id = Laser.MenuId, + priority = 1, + }) + +end) + +Hooks:Add("MenuManagerBuildCustomMenus", "MenuManagerBuildCustomMenus_EnemyLaser", function(menu_manager, mainmenu_nodes) + local menu_id = Laser.MenuId + local data = { + area_bg = "half" + } + mainmenu_nodes[menu_id] = MenuHelper:BuildMenu( menu_id, data ) +end) + +-- Hooks +Hooks:Add("WeaponLaserPostSetColorByTheme", "WeaponLaserPostSetColorByTheme_CustomEnemyLaser", function(laser, unit) + + if not Laser:IsEnabled() then + return + end + + if not laser._is_npc or Laser:IsNPCPlayerUnitLaser( laser ) then + return + end + + laser:set_color( Laser:GetColor() ) + +end) + +Hooks:Add("WeaponLaserUpdate", "WeaponLaserUpdate_EnemyRainbow", function(laser, unit, t, dt) + + if not Laser:IsEnabled() then + return + end + + if not laser._is_npc or Laser:IsNPCPlayerUnitLaser( laser ) then + return + end + + if not Laser:IsRainbow() then + laser:set_color( Laser:GetColor() ) + end + + if Laser:IsRainbow() then + Laser:GetColor() + local r, g, b = Laser.Color:ToRGB( math.sin(GoonBase.Options.EnemyLaser.RainbowSpeed * t), GoonBase.Options.EnemyLaser.G, GoonBase.Options.EnemyLaser.B ) + laser:set_color( Color(r, g, b) ) + end + +end) diff --git a/GoonMod/mods/disabled/colors/weapon_flashlight.lua b/GoonMod/mods/disabled/colors/weapon_flashlight.lua new file mode 100644 index 0000000..df3f743 --- /dev/null +++ b/GoonMod/mods/disabled/colors/weapon_flashlight.lua @@ -0,0 +1,194 @@ + +-- Mod Definition +local Mod = class( BaseMod ) +Mod.id = "CustomWeaponFlashlight" +Mod.Name = "Custom Weapon Flashlight Colour" +Mod.Desc = "Set a custom colour for flashlights attached to your weapons" +Mod.Requirements = {} +Mod.Incompatibilities = {} +Mod.Priority = 1 + +Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() + GoonBase.Mods:RegisterMod( Mod ) +end) + +if not Mod:IsEnabled() then + return +end + +-- Weapon Light +GoonBase.WeaponLight = GoonBase.WeaponLight or {} +local Light = GoonBase.WeaponLight +Light.MenuId = "goonbase_weapon_light_menu" +Light.Color = nil + +-- Options +if GoonBase.Options.WeaponLight == nil then + GoonBase.Options.WeaponLight = {} + GoonBase.Options.WeaponLight.Enabled = false + GoonBase.Options.WeaponLight.R = 1 + GoonBase.Options.WeaponLight.G = 0.1 + GoonBase.Options.WeaponLight.B = 0.1 + GoonBase.Options.WeaponLight.HSV = false + GoonBase.Options.WeaponLight.Rainbow = false + GoonBase.Options.WeaponLight.RainbowSpeed = 10 +end + +-- Functions +function Light:IsEnabled() + return GoonBase.Options.WeaponLight.Enabled +end + +function Light:IsRainbow() + return GoonBase.Options.WeaponLight.Rainbow +end + +function Light:GetColor(alpha) + if Light.Color == nil then + Light.Color = ColorHSVRGB:new() + Light.Color:SetOptionsTable( "WeaponLight" ) + end + return Light.Color:GetColor( alpha ) or Color( alpha or 1, 1, 0, 0 ) +end + +-- Menu +Hooks:Add("MenuManagerSetupCustomMenus", "MenuManagerSetupCustomMenus_WeaponLight", function(menu_manager, menu_nodes) + MenuHelper:NewMenu( Light.MenuId ) +end) + +Hooks:Add("MenuManagerSetupGoonBaseMenu", "MenuManagerSetupGoonBaseMenu_WeaponLight", function(menu_manager, menu_nodes) + + -- Submenu Button + MenuHelper:AddButton({ + id = "weapon_light_button", + title = "Options_WeaponLightName", + desc = "Options_WeaponLightDesc", + next_node = Light.MenuId, + menu_id = "goonbase_options_menu", + }) + + -- Enabled Toggle + MenuCallbackHandler.toggle_custom_weapon_light_color = function(this, item) + GoonBase.Options.WeaponLight.Enabled = item:value() == "on" and true or false + GoonBase.Options:Save() + end + + MenuHelper:AddToggle({ + id = "toggle_custom_weapon_light_color", + title = "Options_WeaponLightEnableTitle", + desc = "Options_WeaponLightEnableDesc", + callback = "toggle_custom_weapon_light_color", + value = GoonBase.Options.WeaponLight.Enabled, + menu_id = Light.MenuId, + priority = 11 + }) + + -- RGB/HSV Colour + Light.Color = ColorHSVRGB:new() + Light.Color:SetID( "weapon_light" ) + Light.Color:SetPriority( 5 ) + Light.Color:SetOptionsTable( "WeaponLight" ) + Light.Color:SetupMenu( Light.MenuId ) + + -- Rainbow Laser + MenuCallbackHandler.toggle_weapon_light_rainbow = function(this, item) + GoonBase.Options.WeaponLight.Rainbow = item:value() == "on" and true or false + GoonBase.Options:Save() + end + + MenuCallbackHandler.weapon_light_rainbow_speed = function(this, item) + GoonBase.Options.WeaponLight.RainbowSpeed = item:value() + GoonBase.Options:Save() + end + + MenuHelper:AddToggle({ + id = "toggle_weapon_light_rainbow", + title = "Options_WeaponLightRainbowTitle", + desc = "Options_WeaponLightRainbowDesc", + callback = "toggle_weapon_light_rainbow", + value = GoonBase.Options.WeaponLight.Rainbow, + menu_id = Light.MenuId, + priority = 2 + }) + + MenuHelper:AddSlider({ + id = "weapon_light_rainbow_speed", + title = "Options_WeaponLightRainbowSpeedTitle", + desc = "Options_WeaponLightRainbowSpeedDesc", + callback = "weapon_light_rainbow_speed", + value = GoonBase.Options.WeaponLight.RainbowSpeed, + min = 1, + max = 100, + step = 1, + show_value = true, + menu_id = Light.MenuId, + priority = 1, + }) + +end) + +Hooks:Add("MenuManagerBuildCustomMenus", "MenuManagerBuildCustomMenus_WeaponLight", function(menu_manager, mainmenu_nodes) + local menu_id = Light.MenuId + local data = { + area_bg = "half" + } + mainmenu_nodes[menu_id] = MenuHelper:BuildMenu( menu_id, data ) +end) + +-- Hooks +Hooks:Add("WeaponFlashLightInit", "WeaponFlashLightInit_CustomLight", function(flashlight, unit) + + if not Light:IsEnabled() then + Hooks:Remove("WeaponFlashLightInit_CustomLight") + return + end + + flashlight._light:set_color( Light:GetColor() ) + +end) + +Hooks:Add("WeaponFlashLightCheckState", "WeaponFlashLightCheckState_CustomLight", function(flashlight) + + if flashlight._on then + + flashlight._unit:set_extension_update_enabled( Idstring("base"), flashlight._on ) + + Hooks:RegisterHook("WeaponFlashLightUpdate") + flashlight._old_update = flashlight.update + flashlight.update = function(self, unit, t, dt) + Hooks:Call( "WeaponFlashLightUpdate", self, unit, t, dt ) + if flashlight.overkill_update ~= nil then + flashlight.overkill_update(self, unit, t, dt) + end + end + + end + +end) + +Hooks:Add("WeaponFlashLightUpdate", "WeaponFlashLightUpdate_Rainbow", function(flashlight, unit, t, dt) + + local psuccess, perror = pcall(function() + + if not Light:IsEnabled() then + return + end + + if not Light:IsRainbow() then + flashlight._light:set_color( Light:GetColor() ) + end + + if Light:IsRainbow() then + + Light:GetColor() + local r, g, b = Light.Color:ToRGB( math.sin(GoonBase.Options.WeaponLight.RainbowSpeed * t), GoonBase.Options.WeaponLight.G, GoonBase.Options.WeaponLight.B ) + flashlight._light:set_color( Color(r * 2, g * 2, b * 2) ) + + end + + end) + if not psuccess then + Print("[Error] " .. perror) + end + +end) diff --git a/GoonMod/mods/disabled/colors/weapon_laser.lua b/GoonMod/mods/disabled/colors/weapon_laser.lua new file mode 100644 index 0000000..0897efb --- /dev/null +++ b/GoonMod/mods/disabled/colors/weapon_laser.lua @@ -0,0 +1,376 @@ + +-- Mod Definition +local Mod = class( BaseMod ) +Mod.id = "CustomWeaponLaser" +Mod.Name = "Custom Weapon Laser Colour" +Mod.Desc = "Set a custom colour for lasers attached to your weapons" +Mod.Requirements = {} +Mod.Incompatibilities = {} +Mod.Priority = 1 + +Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() + GoonBase.Mods:RegisterMod( Mod ) +end) + +if not Mod:IsEnabled() then + return +end + +-- Weapon Laser +GoonBase.WeaponLaser = GoonBase.WeaponLaser or {} +local Laser = GoonBase.WeaponLaser +Laser.MenuId = "goonbase_weapon_laser_menu" +Laser.Color = nil +Laser.UniquePlayerColours = { + [1] = Color("29ce31"), -- MrGreen + [2] = Color("00eae8"), -- MrBlue + [3] = Color("f99d1c"), -- MrBrown + [4] = Color("ebe818"), -- MrOrange + [5] = Color("ebe818"), -- MrAI +} +Laser.OtherColours = {} + +-- Networking +Laser.Network = Laser.Network or {} +local Network = Laser.Network +Network.SendLaserColour = "CustomLaserColour" + +-- Options +if GoonBase.Options.WeaponLaser == nil then + GoonBase.Options.WeaponLaser = {} + GoonBase.Options.WeaponLaser.Enabled = false + GoonBase.Options.WeaponLaser.R = 1 + GoonBase.Options.WeaponLaser.G = 0.1 + GoonBase.Options.WeaponLaser.B = 0.1 + GoonBase.Options.WeaponLaser.HSV = false + GoonBase.Options.WeaponLaser.Rainbow = false + GoonBase.Options.WeaponLaser.RainbowSpeed = 10 + GoonBase.Options.WeaponLaser.TeammateLasers = 1 +end + +-- Functions +function Laser:IsEnabled() + return GoonBase.Options.WeaponLaser.Enabled +end + +function Laser:IsRainbow() + return GoonBase.Options.WeaponLaser.Rainbow +end + +function Laser:GetColor( alpha ) + if Laser.Color == nil then + Laser.Color = ColorHSVRGB:new() + Laser.Color:SetOptionsTable( "WeaponLaser" ) + end + return Laser.Color:GetColor( alpha ) or Color( alpha or 1, 1, 0, 0 ) +end + +function Laser:GetCriminalNameFromLaserUnit( laser ) + + if not self._laser_units_lookup then + self._laser_units_lookup = {} + end + + local laser_key = nil + if laser._unit then + laser_key = laser._unit:key() + end + if laser_key and self._laser_units_lookup[laser_key] ~= nil then + return self._laser_units_lookup[laser_key] + end + + local criminals_manager = managers.criminals + if not criminals_manager then + return + end + + for id, data in pairs(criminals_manager._characters) do + if data.unit ~= nil and alive(data.unit) and data.name ~= criminals_manager:local_character_name() then + + if data.unit:inventory() and data.unit:inventory():equipped_unit() then + + local wep_base = data.unit:inventory():equipped_unit():base() + if wep_base then + + if wep_base._factory_id ~= nil and wep_base._blueprint ~= nil then + + local gadgets = managers.weapon_factory:get_parts_from_weapon_by_type_or_perk("gadget", wep_base._factory_id, wep_base._blueprint) + if gadgets then + local gadget + for _, i in ipairs(gadgets) do + + gadget = wep_base._parts[i] + gadget = gadget.unit:base() + + if gadget == laser then + if laser_key then + self._laser_units_lookup[laser_key] = data.name + end + return data.name + end + + end + end + + end + + end + + end + + end + end + + if laser_key then + self._laser_units_lookup[laser_key] = false + end + return nil + +end + +function Laser:UsingSameColour() + return GoonBase.Options.WeaponLaser.TeammateLasers == 1 +end + +function Laser:UsingUniqueColour() + return GoonBase.Options.WeaponLaser.TeammateLasers == 2 +end + +function Laser:UsingPlayerColour() + return GoonBase.Options.WeaponLaser.TeammateLasers == 3 +end + +-- Menu +Hooks:Add("MenuManagerSetupCustomMenus", "MenuManagerSetupCustomMenus_" .. Mod:ID(), function(menu_manager, menu_nodes) + MenuHelper:NewMenu( Laser.MenuId ) +end) + +Hooks:Add("MenuManagerSetupGoonBaseMenu", "MenuManagerSetupGoonBaseMenu_" .. Mod:ID(), function(menu_manager, menu_nodes) + + -- Submenu Button + MenuHelper:AddButton({ + id = "weapon_laser_button", + title = "Options_WeaponLaserName", + desc = "Options_WeaponLaserDesc", + next_node = Laser.MenuId, + menu_id = "goonbase_options_menu", + }) + + -- Enabled Toggle + MenuCallbackHandler.toggle_custom_weapon_laser_color = function(this, item) + GoonBase.Options.WeaponLaser.Enabled = item:value() == "on" and true or false + GoonBase.Options:Save() + end + + MenuHelper:AddToggle({ + id = "toggle_custom_weapon_laser_color", + title = "Options_WeaponLaserEnableTitle", + desc = "Options_WeaponLaserEnableDesc", + callback = "toggle_custom_weapon_laser_color", + value = GoonBase.Options.WeaponLaser.Enabled, + menu_id = Laser.MenuId, + priority = 50 + }) + + -- RGB/HSV Colour + Laser.Color = ColorHSVRGB:new() + Laser.Color:SetID( "weapon_laser" ) + Laser.Color:SetPriority( 30 ) + Laser.Color:SetOptionsTable( "WeaponLaser" ) + Laser.Color:SetupMenu( Laser.MenuId ) + + -- Rainbow Laser + MenuCallbackHandler.toggle_weapon_laser_rainbow = function(this, item) + GoonBase.Options.WeaponLaser.Rainbow = item:value() == "on" and true or false + GoonBase.Options:Save() + end + + MenuCallbackHandler.weapon_laser_rainbow_speed = function(this, item) + GoonBase.Options.WeaponLaser.RainbowSpeed = item:value() + GoonBase.Options:Save() + end + + MenuCallbackHandler.weapon_laser_set_teammate = function(this, item) + GoonBase.Options.WeaponLaser.TeammateLasers = tonumber(item:value()) + GoonBase.Options:Save() + end + + MenuHelper:AddToggle({ + id = "toggle_weapon_laser_rainbow", + title = "Options_WeaponLaserRainbowTitle", + desc = "Options_WeaponLaserRainbowDesc", + callback = "toggle_weapon_laser_rainbow", + value = GoonBase.Options.WeaponLaser.Rainbow, + menu_id = Laser.MenuId, + priority = 25 + }) + + MenuHelper:AddSlider({ + id = "weapon_laser_rainbow_speed", + title = "Options_WeaponLaserRainbowSpeedTitle", + desc = "Options_WeaponLaserRainbowSpeedDesc", + callback = "weapon_laser_rainbow_speed", + value = GoonBase.Options.WeaponLaser.RainbowSpeed, + min = 1, + max = 100, + step = 1, + show_value = true, + menu_id = Laser.MenuId, + priority = 24, + }) + + MenuHelper:AddDivider({ + id = "weapon_laser_divider", + menu_id = Laser.MenuId, + size = 16, + priority = 21, + }) + + MenuHelper:AddMultipleChoice({ + id = "weapon_laser_teammate_choice", + title = "Options_TeammateLaserOption", + desc = "Options_TeammateLaserOptionDesc", + callback = "weapon_laser_set_teammate", + menu_id = Laser.MenuId, + priority = 20, + items = { + [1] = "Options_TeammateLaser_Same", + [2] = "Options_TeammateLaser_Unique", + [3] = "Options_TeammateLaser_Theirs", + }, + value = GoonBase.Options.WeaponLaser.TeammateLasers, + }) + +end) + +Hooks:Add("MenuManagerBuildCustomMenus", "MenuManagerBuildCustomMenus_" .. Mod:ID(), function(menu_manager, mainmenu_nodes) + local menu_id = Laser.MenuId + local data = { + area_bg = "half" + } + mainmenu_nodes[menu_id] = MenuHelper:BuildMenu( menu_id, data ) +end) + +-- Hooks +Hooks:Add("WeaponLaserInit", "WeaponLaserInit_" .. Mod:ID(), function(laser, unit) + Laser:UpdateLaser(laser, unit, 0, 0) +end) + +Hooks:Add("WeaponLaserUpdate", "WeaponLaserUpdate_Rainbow_" .. Mod:ID(), function(laser, unit, t, dt) + Laser:UpdateLaser(laser, unit, t, dt) +end) + +function Laser:UpdateLaser( laser, unit, t, dt ) + + local psuccess, perror = pcall(function() + + if not Laser:IsEnabled() then + return + end + + if laser._is_npc then + + local criminal_name = Laser:GetCriminalNameFromLaserUnit( laser ) + if not criminal_name then + return + end + + if Laser:UsingSameColour() then + Laser:SetColourOfLaser( laser, unit, t, dt ) + end + + if Laser:UsingUniqueColour() then + local id = managers.criminals:character_color_id_by_name( criminal_name ) + if id == 1 then id = id + 1 end + local col = Laser.UniquePlayerColours[ id or 5 ] + Laser:SetColourOfLaser( laser, unit, t, dt, col ) + end + + if Laser:UsingPlayerColour() then + local col = Laser.OtherColours[criminal_name] + Laser:SetColourOfLaser( laser, unit, t, dt, col ) + end + + return + + end + + Laser:SetColourOfLaser( laser, unit, t, dt ) + + end) + if not psuccess then + Print("[Error] " .. perror) + end + +end + +function Laser:SetColourOfLaser( laser, unit, t, dt, colour_override ) + + if colour_override ~= nil and colour_override ~= "rainbow" then + local psuccess, perror = pcall(function() + laser:set_color( colour_override:with_alpha(0.4) ) + end) + if not psuccess then + Print("[Error] " .. perror) + end + return + end + + if not Laser:IsRainbow() then + laser:set_color( Laser:GetColor(0.4) ) + end + + if Laser:IsRainbow() or colour_override == "rainbow" then + Laser:GetColor() + local r, g, b = Laser.Color:ToRGB( math.sin(GoonBase.Options.WeaponLaser.RainbowSpeed * t), GoonBase.Options.WeaponLaser.G, GoonBase.Options.WeaponLaser.B ) + laser:set_color( Color(r, g, b):with_alpha(0.4) ) + end + +end + +-- Networked Colour +Hooks:Add("WeaponLaserSetOn", "WeaponLaserSetOn_" .. Mod:ID(), function(laser) + + if laser._is_npc then + return + end + + local criminals_manager = managers.criminals + if not criminals_manager then + return + end + + local local_name = criminals_manager:local_character_name() + local laser_name = Laser:GetCriminalNameFromLaserUnit( laser ) + if laser_name == nil or local_name == laser_name then + local col_str = LuaNetworking:PrepareNetworkedColourString( Laser:GetColor() ) + if Laser:IsRainbow() then + col_str = "rainbow" + end + LuaNetworking:SendToPeers( Network.SendLaserColour, col_str ) + end + +end) + +Hooks:Add("NetworkReceivedData", "NetworkReceivedData_" .. Mod:ID(), function(sender, message, data) + + if message == Network.SendLaserColour then + + local criminals_manager = managers.criminals + if not criminals_manager then + return + end + + local char = criminals_manager:character_name_by_peer_id(sender) + local col = data + if data ~= "rainbow" then + col = LuaNetworking:NetworkedColourStringToColour(data) + end + + if char then + Laser.OtherColours[char] = col + end + + end + +end) diff --git a/GoonMod/mods/disabled/colors/world_laser_colors.lua b/GoonMod/mods/disabled/colors/world_laser_colors.lua new file mode 100644 index 0000000..324a104 --- /dev/null +++ b/GoonMod/mods/disabled/colors/world_laser_colors.lua @@ -0,0 +1,165 @@ + +-- Mod Definition +local Mod = class( BaseMod ) +Mod.id = "CustomWorldLaserColour" +Mod.Name = "Custom World Laser Colour" +Mod.Desc = "Set a custom colour for lasers that appear in the game world" +Mod.Requirements = {} +Mod.Incompatibilities = {} +Mod.Priority = 1 + +Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() + GoonBase.Mods:RegisterMod( Mod ) +end) + +if not Mod:IsEnabled() then + return +end + +-- Lasers +GoonBase.WorldLasers = GoonBase.WorldLasers or {} +local Lasers = GoonBase.WorldLasers +Lasers.MenuId = "goonbase_world_laser_menu" +Lasers.Color = nil + +-- Options +if GoonBase.Options.WorldLasers == nil then + GoonBase.Options.WorldLasers = {} + GoonBase.Options.WorldLasers.Enabled = false + GoonBase.Options.WorldLasers.R = 1 + GoonBase.Options.WorldLasers.G = 0.1 + GoonBase.Options.WorldLasers.B = 0.1 + GoonBase.Options.WorldLasers.HSV = false + GoonBase.Options.WorldLasers.Rainbow = false + GoonBase.Options.WorldLasers.RainbowSpeed = 10 +end + +-- Functions +function Lasers:IsEnabled() + return GoonBase.Options.WorldLasers.Enabled +end + +function Lasers:IsRainbow() + return GoonBase.Options.WorldLasers.Rainbow +end + +function Lasers:GetColor(alpha) + if Lasers.Color == nil then + Lasers.Color = ColorHSVRGB:new() + Lasers.Color:SetOptionsTable( "WorldLasers" ) + end + return Lasers.Color:GetColor( alpha ) or Color( alpha or 1, 1, 0, 0 ) +end + +-- Menu +Hooks:Add("MenuManagerSetupCustomMenus", "MenuManagerSetupCustomMenus_WorldLaser", function(menu_manager, menu_nodes) + MenuHelper:NewMenu( Lasers.MenuId ) +end) + +Hooks:Add("MenuManagerSetupGoonBaseMenu", "MenuManagerSetupGoonBaseMenu_WorldLaser", function(menu_manager, menu_nodes) + + -- Submenu Button + MenuHelper:AddButton({ + id = "world_laser_button", + title = "Options_WorldLaserName", + desc = "Options_WorldLaserDesc", + next_node = Lasers.MenuId, + menu_id = "goonbase_options_menu", + }) + + -- Enabled Toggle + MenuCallbackHandler.toggle_custom_world_laser_color = function(this, item) + GoonBase.Options.WorldLasers.Enabled = item:value() == "on" and true or false + GoonBase.Options:Save() + end + + MenuHelper:AddToggle({ + id = "toggle_custom_world_laser_color", + title = "Options_WorldLaserEnableTitle", + desc = "Options_WorldLaserEnableDesc", + callback = "toggle_custom_world_laser_color", + value = GoonBase.Options.WorldLasers.Enabled, + menu_id = Lasers.MenuId, + priority = 11 + }) + + -- RGB/HSV Colour + Lasers.Color = ColorHSVRGB:new() + Lasers.Color:SetID( "world_laser" ) + Lasers.Color:SetPriority( 5 ) + Lasers.Color:SetOptionsTable( "WorldLasers" ) + Lasers.Color:SetupMenu( Lasers.MenuId ) + + -- Rainbow Laser + MenuCallbackHandler.toggle_world_laser_rainbow = function(this, item) + GoonBase.Options.WorldLasers.Rainbow = item:value() == "on" and true or false + GoonBase.Options:Save() + end + + MenuCallbackHandler.world_laser_rainbow_speed = function(this, item) + GoonBase.Options.WorldLasers.RainbowSpeed = item:value() + GoonBase.Options:Save() + end + + MenuHelper:AddToggle({ + id = "toggle_world_laser_rainbow", + title = "Options_WorldLaserRainbowTitle", + desc = "Options_WorldLaserRainbowDesc", + callback = "toggle_world_laser_rainbow", + value = GoonBase.Options.WorldLasers.Rainbow, + menu_id = Lasers.MenuId, + priority = 2 + }) + + MenuHelper:AddSlider({ + id = "world_laser_rainbow_speed", + title = "Options_WorldLaserRainbowSpeedTitle", + desc = "Options_WorldLaserRainbowSpeedDesc", + callback = "world_laser_rainbow_speed", + value = GoonBase.Options.WorldLasers.RainbowSpeed, + min = 1, + max = 100, + step = 1, + show_value = true, + menu_id = Lasers.MenuId, + priority = 1, + }) + +end) + +Hooks:Add("MenuManagerBuildCustomMenus", "MenuManagerBuildCustomMenus_WorldLaser", function(menu_manager, mainmenu_nodes) + local menu_id = Lasers.MenuId + local data = { + area_bg = "half" + } + mainmenu_nodes[menu_id] = MenuHelper:BuildMenu( menu_id, data ) +end) + +-- Hooks +Hooks:Add("ElementLaserTriggerPostInit", "ElementLaserTriggerPostInit_WorldLaser", function(laser) + + if not Lasers:IsEnabled() then + return + end + + laser._brush:set_color( Lasers:GetColor(0.15) ) + +end) + +Hooks:Add("ElementLaserTriggerUpdateDraw", "ElementLaserTriggerUpdateDraw_WorldLaser", function(laser, t, dt) + + if not Lasers:IsEnabled() then + return + end + + if not Lasers:IsRainbow() then + laser._brush:set_color( Lasers:GetColor() ) + end + + if Lasers:IsRainbow() then + Lasers:GetColor() + local r, g, b = Lasers.Color:ToRGB( math.sin(GoonBase.Options.WorldLasers.RainbowSpeed * t), GoonBase.Options.WorldLasers.G, GoonBase.Options.WorldLasers.B ) + laser._brush:set_color( Color(0.2, r, g, b) ) + end + +end) diff --git a/GoonMod/mods/disabled/custom_waypoints.lua b/GoonMod/mods/disabled/custom_waypoints.lua new file mode 100644 index 0000000..697949c --- /dev/null +++ b/GoonMod/mods/disabled/custom_waypoints.lua @@ -0,0 +1,259 @@ + +-- Mod Definition +local Mod = class( BaseMod ) +Mod.id = "CustomWaypoints" +Mod.Name = "Custom Waypoints" +Mod.Desc = "Allows players to set waypoints for themselves and friends" +Mod.Requirements = {} +Mod.Incompatibilities = {} + +Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() + GoonBase.Mods:RegisterMod( Mod ) +end) + +if not Mod:IsEnabled() then + return +end + +-- Custom Waypoints +_G.GoonBase.CustomWaypoints = _G.GoonBase.CustomWaypoints or {} +local CustomWaypoints = _G.GoonBase.CustomWaypoints +CustomWaypoints.MenuID = "goonbase_custom_waypoints_menu" +CustomWaypoints.PlaceWaypointName = "GoonBasePlaceWaypoint" +CustomWaypoints.RemoveWaypointName = "GoonBaseRemoveWaypoint" +CustomWaypoints.CustomKeys = { + PLACE = GoonBase.Options.CustomWaypoints ~= nil and GoonBase.Options.CustomWaypoints.PlaceWaypoint or "k", + REMOVE = GoonBase.Options.CustomWaypoints ~= nil and GoonBase.Options.CustomWaypoints.RemoveWaypoint or "l" +} + +-- Network +CustomWaypoints.Network = {} +CustomWaypoints.Network.PlaceWaypoint = "CustomWaypointPlace" +CustomWaypoints.Network.RemoveWaypoint = "CustomWaypointRemove" + +-- Options +if GoonBase.Options.CustomWaypoints == nil then + GoonBase.Options.CustomWaypoints = {} + GoonBase.Options.CustomWaypoints.PlaceWaypoint = "k" + GoonBase.Options.CustomWaypoints.RemoveWaypoint = "l" + GoonBase.Options.CustomWaypoints.ShowDistance = true +end + +-- Updates +Hooks:Add("GameSetupUpdate", "GameSetupUpdate_" .. Mod:ID(), function(t, dt) + CustomWaypoints:UpdateBindings() +end) + +function CustomWaypoints:UpdateBindings() + + local self = CustomWaypoints + if self._input == nil then + self._input = Input:keyboard() + end + if managers.hud:chat_focus() then + return + end + + local place_key = CustomWaypoints.CustomKeys.PLACE + if not string.is_nil_or_empty(place_key) and self._input:pressed(Idstring(place_key)) then + CustomWaypoints:SetWaypoint() + end + + local remove_key = CustomWaypoints.CustomKeys.REMOVE + if not string.is_nil_or_empty(remove_key) and self._input:pressed(Idstring(remove_key)) then + CustomWaypoints:RemoveWaypoint() + end + +end + +-- Custom Key Set +Hooks:Add("CustomizeControllerOnKeySet", "CustomizeControllerOnKeySet_" .. Mod:ID(), function(item) + + if item._name == CustomWaypoints.PlaceWaypointName then + CustomWaypoints.CustomKeys.PLACE = item._input_name_list[1] + CustomWaypoints:SaveBindings() + end + + if item._name == CustomWaypoints.RemoveWaypointName then + CustomWaypoints.CustomKeys.REMOVE = item._input_name_list[1] + CustomWaypoints:SaveBindings() + end + +end) + +function CustomWaypoints:SaveBindings() + GoonBase.Options.CustomWaypoints.PlaceWaypoint = CustomWaypoints.CustomKeys.PLACE + GoonBase.Options.CustomWaypoints.RemoveWaypoint = CustomWaypoints.CustomKeys.REMOVE + GoonBase.Options:Save() +end + +-- Menu +Hooks:Add("MenuManagerSetupCustomMenus", "MenuManagerSetupCustomMenus_" .. Mod:ID(), function(menu_manager, menu_nodes) + MenuHelper:NewMenu( CustomWaypoints.MenuID ) +end) + +Hooks:Add("MenuManagerSetupGoonBaseMenu", "MenuManagerSetupGoonBaseMenu_" .. Mod:ID(), function( menu_manager ) + + -- Menu button + MenuHelper:AddButton({ + id = "custom_waypoint_menu_button", + title = "OptionsMenu_CustomWaypointMenuTitle", + desc = "OptionsMenu_CustomWaypointMenuMessage", + next_node = CustomWaypoints.MenuID, + menu_id = "goonbase_options_menu" + }) + + -- Keybinds + MenuHelper:AddKeybinding({ + id = "custom_waypoint_menu_keybind_place", + title = managers.localization:text("OptionsMenu_CustomWaypointKeybindPlace"), + connection_name = CustomWaypoints.PlaceWaypointName, + button = CustomWaypoints.CustomKeys.PLACE, + binding = CustomWaypoints.CustomKeys.PLACE, + menu_id = CustomWaypoints.MenuID, + priority = 10 + }) + + MenuHelper:AddKeybinding({ + id = "custom_waypoint_menu_keybind_remove", + title = managers.localization:text("OptionsMenu_CustomWaypointKeybindRemove"), + connection_name = CustomWaypoints.RemoveWaypointName, + button = CustomWaypoints.CustomKeys.REMOVE, + binding = CustomWaypoints.CustomKeys.REMOVE, + menu_id = CustomWaypoints.MenuID, + priority = 9 + }) + + -- Show Distance + MenuCallbackHandler.toggle_custom_waypoint_distance = function(this, item) + GoonBase.Options.CustomWaypoints.ShowDistance = item:value() == "on" and true or false + GoonBase.Options:Save() + end + + MenuHelper:AddDivider({ + id = "custom_waypoint_menu_divider", + menu_id = CustomWaypoints.MenuID, + size = 16, + priority = 2, + }) + + MenuHelper:AddToggle({ + id = "toggle_custom_waypoint_distance", + title = "OptionsMenu_CustomWaypointShowDistanceTitle", + desc = "OptionsMenu_CustomWaypointShowDistanceMessage", + callback = "toggle_custom_waypoint_distance", + value = GoonBase.Options.CustomWaypoints.ShowDistance, + menu_id = CustomWaypoints.MenuID, + priority = 1, + }) + +end) + +Hooks:Add("MenuManagerBuildCustomMenus", "MenuManagerBuildCustomMenus_" .. Mod:ID(), function(menu_manager, mainmenu_nodes) + mainmenu_nodes[CustomWaypoints.MenuID] = MenuHelper:BuildMenu( CustomWaypoints.MenuID ) +end) + +-- Waypoints +function CustomWaypoints:_AddWaypoint( waypoint_name, pos, color_id ) + managers.hud:add_waypoint( + "CustomWaypoint_" .. waypoint_name, + { + icon = "infamy_icon", + distance = GoonBase.Options.CustomWaypoints.ShowDistance, + position = pos, + no_sync = false, + present_timer = 0, + state = "present", + radius = 50, + color = tweak_data.preplanning_peer_colors[color_id or 1], + blend_mode = "add" + } + ) +end + +function CustomWaypoints:_RemoveWaypoint( waypoint_name ) + managers.hud:remove_waypoint("CustomWaypoint_" .. waypoint_name) +end + +function CustomWaypoints:SetWaypoint() + + if managers.player:player_unit() == nil then + return + end + + local psuccess, perror = pcall(function() + + local pos = GetPlayerAimPos( managers.player:player_unit() ) + if not pos then + return + end + + CustomWaypoints:_AddWaypoint( "localplayer", pos, GoonBase.Network:LocalPeerID() ) + + pos = Vector3.ToString( pos ) + GoonBase.Network:SendToPeers( CustomWaypoints.Network.PlaceWaypoint, pos ) + + end) + if not psuccess then + Print("[Error] " .. perror) + end + +end + +function CustomWaypoints:RemoveWaypoint() + + local psuccess, perror = pcall(function() + + GoonBase.Network:SendToPeers( CustomWaypoints.Network.RemoveWaypoint, "" ) + CustomWaypoints:_RemoveWaypoint( "localplayer" ) + + end) + if not psuccess then + Print("[Error] " .. perror) + end + +end + +function CustomWaypoints:NetworkPlace( player, position ) + + local psuccess, perror = pcall(function() + + local ply_name = GoonBase.Network:GetNameFromPeerID(player) + local pos = string.ToVector3(position) + if pos ~= nil then + CustomWaypoints:_AddWaypoint( ply_name, pos, player ) + end + + end) + if not psuccess then + Print("[Error] " .. perror) + end + +end + +function CustomWaypoints:NetworkRemove(player) + + local psuccess, perror = pcall(function() + + local ply_name = GoonBase.Network:GetNameFromPeerID(player) + CustomWaypoints:_RemoveWaypoint( ply_name ) + + end) + if not psuccess then + Print("[Error] " .. perror) + end + +end + +-- Networked Data +Hooks:Add("NetworkReceivedData", "NetworkReceivedData_" .. Mod:ID(), function(sender, messageType, data) + + if messageType == CustomWaypoints.Network.PlaceWaypoint then + CustomWaypoints:NetworkPlace(sender, data) + end + + if messageType == CustomWaypoints.Network.RemoveWaypoint then + CustomWaypoints:NetworkRemove(sender) + end + +end) diff --git a/GoonMod/mods/disabled/extended_inventory.lua b/GoonMod/mods/disabled/extended_inventory.lua new file mode 100644 index 0000000..175f4fd --- /dev/null +++ b/GoonMod/mods/disabled/extended_inventory.lua @@ -0,0 +1,324 @@ + +-- Mod Definition +local Mod = class( BaseMod ) +Mod.id = "ExtendedInventory" +Mod.Name = "Extended Inventory" +Mod.Desc = "Allows mods to add special items to your inventory" +Mod.Requirements = {} +Mod.Incompatibilities = {} + +Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() + GoonBase.Mods:RegisterMod( Mod ) +end) + +if not Mod:IsEnabled() then + return +end + +-- Extended Inventory +_G.GoonBase.ExtendedInventory = _G.GoonBase.ExtendedInventory or {} +local ExtendedInv = _G.GoonBase.ExtendedInventory +ExtendedInv.InitialLoadComplete = false +ExtendedInv.RegisteredItems = {} +ExtendedInv.Items = {} +ExtendedInv.SaveFile = GoonBase.Path .. "inventory.ini" + +-- Initialize +Hooks:RegisterHook("ExtendedInventoryInitialized") +Hooks:Add("GoonBasePostLoadedMods", "GoonBasePostLoadedMods_ExtendedInv", function() + Hooks:Call("ExtendedInventoryInitialized") +end) + +function ExtendedInv:_MissingItemError(item) + Print("[Error] Could not find item '" .. item .. "' in Extended Inventory!") +end + +function ExtendedInv:ItemIsRegistered(id) + return ExtendedInv.RegisteredItems[id] == true +end + +function ExtendedInv:RegisterItem(data) + + if not ExtendedInv.InitialLoadComplete then + ExtendedInv:Load() + ExtendedInv.InitialLoadComplete = true + end + + ExtendedInv.RegisteredItems[data.id] = true + ExtendedInv.Items[data.id] = ExtendedInv.Items[data.id] or {} + for k, v in pairs( data ) do + ExtendedInv.Items[data.id][k] = v + end + ExtendedInv.Items[data.id].amount = ExtendedInv.Items[data.id].amount or 0 + +end + +function ExtendedInv:AddItem(item, amount) + if ExtendedInv.Items[item] ~= nil then + ExtendedInv.Items[item].amount = ExtendedInv.Items[item].amount + amount + ExtendedInv:Save() + else + ExtendedInv:_MissingItemError(item) + end +end + +function ExtendedInv:TakeItem(item, amount) + if ExtendedInv.Items[item] ~= nil then + ExtendedInv.Items[item].amount = ExtendedInv.Items[item].amount - amount + ExtendedInv:Save() + else + ExtendedInv:_MissingItemError(item) + end +end + +function ExtendedInv:SetItemAmount(item, amount) + if ExtendedInv.Items[item] ~= nil then + ExtendedInv.Items[item].amount = amount + ExtendedInv:Save() + else + ExtendedInv:_MissingItemError(item) + end +end + + +function ExtendedInv:GetItem(item) + return ExtendedInv.Items[item] or nil +end + +function ExtendedInv:HasItem(item) + if ExtendedInv.Items[item] == nil then + return false + end + return ExtendedInv.Items[item].amount > 0 or nil +end + +function ExtendedInv:GetAllItems() + return ExtendedInv.Items +end + +function ExtendedInv:GetReserveText(item) + return item.reserve_text or managers.localization:text("bm_ex_inv_in_reserve") +end + +Hooks:Add("BlackMarketGUIPostSetup", "BlackMarketGUIPostSetup_ExtendedInventory", function(gui, is_start_page, component_data) + gui.identifiers.extended_inv = Idstring("extended_inv") +end) + +function ExtendedInv.do_populate_extended_inventory(self, data) + + local psuccess, perror = pcall(function() + + local new_data = {} + local guis_catalog = "guis/" + local index = 0 + + for i, item_data in pairs( ExtendedInv:GetAllItems() ) do + + local hide = item_data.hide_when_none_in_stock or false + if ExtendedInv:ItemIsRegistered(i) and (hide == false or (hide == true and item_data.amount > 0)) then + + local item_id = item_data.id + local name_id = item_data.name + local desc_id = item_data.desc + local texture_id = item_data.texture + + index = index + 1 + new_data = {} + new_data.name = item_id + new_data.name_localized = managers.localization:text( name_id ) + new_data.desc_localized = managers.localization:text( desc_id ) + new_data.category = "extended_inv" + new_data.slot = index + new_data.amount = item_data.amount + new_data.unlocked = (new_data.amount or 0) > 0 + new_data.level = item_data.level or 0 + new_data.skill_based = new_data.level == 0 + new_data.skill_name = new_data.level == 0 and "bm_menu_skill_locked_" .. new_data.name + new_data.bitmap_texture = texture_id + new_data.lock_texture = self:get_lock_icon(new_data) + data[index] = new_data + + end + + end + + -- Fill empty slots + local max_items = data.override_slots[1] * data.override_slots[2] + for i = 1, max_items do + if not data[i] then + new_data = {} + new_data.name = "empty" + new_data.name_localized = "" + new_data.desc_localized = "" + new_data.category = "extended_inv" + new_data.slot = i + new_data.unlocked = true + new_data.equipped = false + data[i] = new_data + end + end + + end) + if not psuccess then + Print("[Error] " .. perror) + end + +end + +Hooks:Add("BlackMarketGUIStartPageData", "BlackMarketGUIStartPageData_ExtendedInventory", function(gui, data) + + local psuccess, perror = pcall(function() + + local should_hide_tab = true + for k, v in pairs( ExtendedInv:GetAllItems() ) do + if v.hide_when_none_in_stock == false or (v.hide_when_none_in_stock == true and v.amount > 0) then + should_hide_tab = false + end + end + if should_hide_tab then + return + end + + gui.populate_extended_inventory = ExtendedInv.do_populate_extended_inventory + + table.insert(data, { + name = "bm_menu_extended_inv", + category = "extended_inv", + on_create_func_name = "populate_extended_inventory", + identifier = gui.identifiers.extended_inv, + override_slots = {5, 2}, + start_crafted_item = 1 + }) + + end) + if not psuccess then + Print("[Error] " .. perror) + end + +end) + +Hooks:Add("BlackMarketGUIUpdateInfoText", "BlackMarketGUIUpdateInfoText_ExtendedInventory", function(gui) + + local psuccess, perror = pcall(function() + + local self = gui + local slot_data = self._slot_data + local tab_data = self._tabs[self._selected]._data + local prev_data = tab_data.prev_node_data + local ids_category = Idstring(slot_data.category) + local identifier = tab_data.identifier + local updated_texts = { + {text = ""}, + {text = ""}, + {text = ""}, + {text = ""}, + {text = ""} + } + + if ids_category == self.identifiers.extended_inv then + + updated_texts[1].text = slot_data.name_localized or "" + updated_texts[2].text = tostring(slot_data.amount or 0) .. " " .. ExtendedInv:GetReserveText(slot_data.name) + updated_texts[4].text = slot_data.desc_localized or "" + + gui:_update_info_text(slot_data, updated_texts) + + end + + end) + if not psuccess then + Print("[Error] " .. perror) + end + +end) + +-- Saving and Loading +function ExtendedInv:GetSaveString() + + local contents = ""; + for k, v in pairs( ExtendedInv.Items ) do + + if type(v) == "table" then + contents = string.format( "%s[%s]\n", contents, tostring(k) ) + for a, b in pairs( v ) do + contents = string.format( "%s%s=%s\n", contents, tostring(a), tostring(b) ) + end + end + + end + + return contents + +end + +function ExtendedInv:Save(fileName) + + if fileName == nil then + fileName = ExtendedInv.SaveFile + end + + -- Encode data using "base64" while saving + -- Simple, but will stop most players from editing the save file since it'll look like gibberish + -- Those who want to cheat that badly will decode any encryption or use lua to bypass it, so + -- no point in super-complicated systems + local file = io.open(fileName, "w+") + local data = ExtendedInv:GetSaveString() + data = GoonBase.Utils.Base64:Encode( data ) + file:write( data ) + file:close() + +end + +function ExtendedInv:Load(fileName) + + if fileName == nil then + fileName = ExtendedInv.SaveFile + end + + local file = io.open(fileName, 'r') + local key + + if file == nil then + Print( "Could not open file (" .. fileName .. ")! Does it exist?" ) + return + end + + local fileString = "" + for line in file:lines() do + fileString = fileString .. line .. "\n" + end + fileString = GoonBase.Utils.Base64:Decode( fileString ) + + local fileLines = string.split(fileString, "[\n]") + for i, line in pairs( fileLines ) do + + local loadKey = line:match('^%[([^%[%]]+)%]$') + if loadKey then + key = tonumber(loadKey) and tonumber(loadKey) or loadKey + ExtendedInv.Items[key] = ExtendedInv.Items[key] or {} + end + + local param, val = line:match('^([%w|_]+)%s-=%s-(.+)$') + if param and val ~= nil then + + if tonumber(val) then + val = tonumber(val) + elseif val == "true" then + val = true + elseif val == "false" then + val = false + end + + if tonumber(param) then + param = tonumber(param) + end + + ExtendedInv.Items[key][param] = val + + end + + end + + file:close() + +end diff --git a/GoonMod/mods/disabled/gage_coins.lua b/GoonMod/mods/disabled/gage_coins.lua new file mode 100644 index 0000000..a5ee259 --- /dev/null +++ b/GoonMod/mods/disabled/gage_coins.lua @@ -0,0 +1,69 @@ + +-- Mod Definition +local Mod = class( BaseMod ) +Mod.id = "GageCoins" +Mod.Name = "Gage-Coins" +Mod.Desc = "Gage has started up his own currency. For every courier assignment you complete, you'll get a whole Gage-Coin from the big man himself" +Mod.Requirements = { "ExtendedInventory" } +Mod.Incompatibilities = {} + +Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() + GoonBase.Mods:RegisterMod( Mod ) +end) + +if not Mod:IsEnabled() then + return +end + +-- Mod Data +_G.GoonBase.GageCoins = _G.GoonBase.GageCoins or {} +local GageCoins = _G.GoonBase.GageCoins +local ExtendedInv = _G.GoonBase.ExtendedInventory +GageCoins.CoinID = "gage_coin" + +-- Hooks +Hooks:Add("ExtendedInventoryInitialized", "ExtendedInventoryInitialized_" .. Mod:ID(), function() + + if ExtendedInv == nil then + return + end + + ExtendedInv:RegisterItem({ + id = GageCoins.CoinID, + name = "GageCoinName", + desc = "GageCoinDesc", + reserve_text = "GageCoinReserve", + texture = "guis/textures/pd2/blackmarket/icons/cash", + hide_when_none_in_stock = true, + }) + +end) + +Hooks:Add("GageAssignmentManagerOnMissionCompleted", "GageAssignmentManagerOnMissionCompleted_" .. Mod:ID(), function(assignment_manager) + + if ExtendedInv == nil then + return + end + + local self = assignment_manager + local total_pickup = 0 + + if self._progressed_assignments then + for assignment, value in pairs(self._progressed_assignments) do + + if value > 0 then + + local collected = Application:digest_value(self._global.active_assignments[assignment], false) + value + local to_aquire = self._tweak_data:get_value(assignment, "aquire") or 1 + while collected >= to_aquire do + collected = collected - to_aquire + ExtendedInv:AddItem(GageCoins.CoinID, 1) + end + + end + + total_pickup = total_pickup + value + end + end + +end) diff --git a/GoonMod/mods/disabled/mod_shop.lua b/GoonMod/mods/disabled/mod_shop.lua new file mode 100644 index 0000000..eeb8746 --- /dev/null +++ b/GoonMod/mods/disabled/mod_shop.lua @@ -0,0 +1,502 @@ + +-- Mod Definition +local Mod = class( BaseMod ) +Mod.id = "BlackmarketModShop" +Mod.Name = "Gage's Mod Shop" +Mod.Desc = "Gage will sell you weapon parts, masks, and mask customization items in return for Gage Coins" +Mod.Requirements = { "ExtendedInventory", "GageCoins" } +Mod.Incompatibilities = {} + +Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() + GoonBase.Mods:RegisterMod( Mod ) +end) + +if not Mod:IsEnabled() then + return +end + +-- Mod Shop +_G.GoonBase.ModShop = _G.GoonBase.ModShop or {} +local ModShop = _G.GoonBase.ModShop +local ExtendedInv = _G.GoonBase.ExtendedInventory + +ModShop.PurchaseCurrency = "gage_coin" +ModShop.CostRegular = 1 +ModShop.CostInfamous = 3 + +ModShop.ExclusionList = { + ["nothing"] = true, + ["no_color_no_material"] = true, + ["no_color_full_material"] = true, + ["plastic"] = true, + ["character_locked"] = true, +} + +ModShop.DLCAlwaysUnlocked = { + ["halloween"] = true, +} + +ModShop.MaskAllowanceList = { + ["normal"] = true, + ["halloween"] = true, + ["infamous"] = true, +} + +ModShop.MaskPricing = { + ["default"] = 5, + ["dlc"] = 5, + ["normal"] = 5, + ["pd2_clan"] = 3, + ["halloween"] = 8, + ["infamous"] = 20, + ["infamy"] = 10, +} + +ModShop.MaskModAllowanceList = { + ["normal"] = true, + ["halloween"] = true, + ["infamous"] = true, +} + +-- Blackmarket Menu +Hooks:Add("BlackMarketGUIPostSetup", "BlackMarketGUIPostSetup_" .. Mod:ID(), function(gui, is_start_page, component_data) + + Hooks:RegisterHook("ModShopAttemptPurchaseWeaponMod") + gui.modshop_purchase_weaponmod_callback = function(self, data) + Hooks:Call("ModShopAttemptPurchaseWeaponMod", data) + end + + Hooks:RegisterHook("ModShopAttemptPurchaseMask") + gui.modshop_purchase_mask_callback = function(self, data) + Hooks:Call("ModShopAttemptPurchaseMask", data) + end + + Hooks:RegisterHook("ModShopAttemptPurchaseMaskPart") + gui.modshop_purchase_mask_part_callback = function(self, data) + Hooks:Call("ModShopAttemptPurchaseMaskPart", data) + end + + local wm_modshop = { + prio = 5, + btn = "BTN_BACK", + pc_btn = Idstring("toggle_chat"), + name = "ModShop_BlackmarketPurchaseWithGageCoins", + callback = callback(gui, gui, "modshop_purchase_weaponmod_callback") + } + + local bm_modshop = { + prio = 5, + btn = "BTN_BACK", + pc_btn = Idstring("toggle_chat"), + name = "ModShop_BlackmarketPurchaseWithGageCoins", + callback = callback(gui, gui, "modshop_purchase_mask_callback") + } + + local mp_modshop = { + prio = 5, + btn = "BTN_BACK", + pc_btn = Idstring("toggle_chat"), + name = "ModShop_BlackmarketPurchaseWithGageCoins", + callback = callback(gui, gui, "modshop_purchase_mask_part_callback") + } + + local btn_x = 10 + gui._btns["wm_modshop"] = BlackMarketGuiButtonItem:new(gui._buttons, wm_modshop, btn_x) + gui._btns["bm_modshop"] = BlackMarketGuiButtonItem:new(gui._buttons, bm_modshop, btn_x) + gui._btns["mp_modshop"] = BlackMarketGuiButtonItem:new(gui._buttons, mp_modshop, btn_x) + +end) + +Hooks:Add("BlackMarketGUIOnPopulateModsActionList", "BlackMarketGUIOnPopulateModsActionList_" .. Mod:ID(), function(gui, data) + if ModShop:WeaponModAllowed(data) then + table.insert(data, "wm_modshop") + end +end) + +function ModShop:WeaponModAllowed(mod) + + if mod == nil then + return false + end + + if mod.free_of_charge then + return false + end + + for k, v in pairs( tweak_data.dlc ) do + if v.achievement_id ~= nil and v.content ~= nil and v.content.loot_drops ~= nil then + for i, loot in pairs( v.content.loot_drops ) do + if loot.item_entry ~= nil and loot.item_entry == mod.name then + return managers.achievment.handler:has_achievement(v.achievement_id) + end + end + end + end + + local gv = mod.global_value + if gv == nil or gv == "normal" then + return true + end + + if not managers.dlc:is_dlc_unlocked(gv) then + return false + end + + return true + +end + +Hooks:Add("BlackMarketGUIOnPopulateBuyMasksActionList", "BlackMarketGUIOnPopulateBuyMasksActionList_" .. Mod:ID(), function(gui, data) + if ModShop:IsMaskOrModAllowed(data, ModShop.MaskAllowanceList) then + table.insert(data, "bm_modshop") + end +end) + +Hooks:Add("BlackMarketGUIOnPopulateMaskModsActionList", "BlackMarketGUIOnPopulateMaskModsActionList_" .. Mod:ID(), function(gui, data) + if ModShop:IsMaskOrModAllowed(data, ModShop.MaskModAllowanceList) then + table.insert(data, "mp_modshop") + end +end) + +function ModShop:IsMaskOrModAllowed(mod, allowance_list) + + if mod == nil then + return false + end + + local gv = mod.global_value + if gv == nil then + return true + end + + if ModShop.ExclusionList[mod.name] == true or ModShop.ExclusionList[gv] == true then + return false + end + + for k, v in pairs( tweak_data.dlc ) do + if v.achievement_id ~= nil and v.content ~= nil and v.content.loot_drops ~= nil then + for i, loot in pairs( v.content.loot_drops ) do + if loot.item_entry ~= nil and loot.item_entry == mod.name then + return managers.achievment.handler:has_achievement(v.achievement_id) + end + end + end + end + + local infamy_lock = mod.infamy_lock + if infamy_lock ~= nil or gv == "infamy" then + local is_unlocked = managers.infamy:owned(infamy_lock) or infamy_lock == nil + if not is_unlocked then + return false + end + end + + if allowance_list and allowance_list[gv] then + return true + end + + if gv ~= "infamy" and not managers.dlc:is_dlc_unlocked(gv) then + return false + end + + return true + +end + +Hooks:Add("BlackMarketManagerModifyGetInventoryCategory", "BlackMarketManagerModifyGetInventoryCategory_" .. Mod:ID(), function(blackmarket, category, data) + + for k, v in pairs( tweak_data.blackmarket[category] ) do + + local already_in_table = false + for x, y in pairs( data ) do + if y.id == k then + already_in_table = true + end + end + + local gv = v.dlc or v.global_value or "normal" + if not already_in_table and ModShop.ExclusionList[k] ~= true then + + if v.infamous then + gv = "infamous" + end + + if gv == "normal" or gv == "infamous" or ( (gv ~= "normal" and managers.dlc:is_dlc_unlocked(gv)) or ModShop.DLCAlwaysUnlocked[gv] == true ) then + table.insert(data, { + id = k, + global_value = gv, + amount = 0 + }) + end + + end + + end + +end) + +-- Purchase Hooks +Hooks:Add("ModShopAttemptPurchaseWeaponMod", "ModShopAttemptPurchaseWeaponMod_" .. Mod:ID(), function(data) + ModShop:SetWeaponModPurchaseData(data) + ModShop:ShowPurchaseMenu() +end) + +Hooks:Add("ModShopAttemptPurchaseMask", "ModShopAttemptPurchaseMask_" .. Mod:ID(), function(data) + ModShop:SetMaskPurchaseData(data) + ModShop:ShowPurchaseMenu() +end) + +Hooks:Add("ModShopAttemptPurchaseMaskPart", "ModShopAttemptPurchaseMaskPart_" .. Mod:ID(), function(data) + ModShop:SetMaskPartPurchaseData(data) + ModShop:ShowPurchaseMenu() +end) + +-- Purchase Menu +function ModShop:SetPurchaseData( data ) + + self._purchase_data = {} + self._purchase_data.name = data.name + self._purchase_data.name_localized = data.name_localized + self._purchase_data.category = data.category + self._purchase_data.global_value = data.global_value + self._purchase_data.cost = ModShop.CostRegular + +end + +function ModShop:SetWeaponModPurchaseData( data ) + + local psuccess, perror = pcall(function() + + self:SetPurchaseData( data ) + + if self:IsWeaponMod( data.category ) then + if data.free_of_charge ~= nil and data.free_of_charge == true then + self._purchase_data.free_of_charge = true + end + end + + end) + if not psuccess then + Print("[Error] " .. perror) + end + +end + +function ModShop:SetMaskPurchaseData( data ) + + local psuccess, perror = pcall(function() + + self:SetPurchaseData( data ) + + if self:IsMask( data.category ) then + + local price = ModShop.MaskPricing[ data.global_value ] or ModShop.MaskPricing["default"] + if data.dlc ~= nil then + price = ModShop.MaskPricing["dlc"] + end + if data.infamy_lock ~= nil then + price = ModShop.MaskPricing["infamy"] + end + + self._purchase_data.cost = price + + end + + end) + if not psuccess then + Print("[Error] " .. perror) + end + +end + +function ModShop:SetMaskPartPurchaseData( data ) + + local psuccess, perror = pcall(function() + + self:SetPurchaseData( data ) + + if self:IsMaskPart( data.category ) then + + local mod_data = tweak_data.blackmarket[data.category][data.name] + if mod_data ~= nil then + + if mod_data.infamous ~= nil and mod_data.infamous == true then + self._purchase_data.cost = ModShop.CostInfamous + end + + if mod_data.global_value == "infamy" or mod_data.infamy_lock ~= nil then + self._purchase_data.cost = ModShop.CostInfamous + end + + end + + end + + end) + if not psuccess then + Print("[Error] " .. perror) + end + +end + +function ModShop:ShowPurchaseMenu() + + if not ExtendedInv then + Print("[Error] Attempting to show purchase menu with no Extended Inventory...") + return + end + + local gage_coins = ExtendedInv:GetItem( ModShop.PurchaseCurrency ) + local purchase_cost = self._purchase_data.cost + + if not gage_coins.amount or type(gage_coins.amount) == "string" then + Print("[Error] Attempting to show purchase menu with no coin amount, or a string as the amount") + return + end + if not purchase_cost or type(purchase_cost) == "string" then + Print("[Error] Attmpting to show purchase menu with a string as the purchasing cost") + return + end + + -- Check if item is free of charge + if self._purchase_data.free_of_charge ~= nil and self._purchase_data.free_of_charge == true then + self:ShowFreeOfCharge() + return + end + + -- Check if we can afford this + if gage_coins.amount < purchase_cost or purchase_cost <= 0 then + self:ShowNotEnoughCoins( purchase_cost ) + return + end + + -- Show purchase menu + local title = managers.localization:text("ModShop_PurchaseWindowTitle") + local message = managers.localization:text("ModShop_PurchaseWindowMessage") + message = message:gsub("{1}", self._purchase_data.name_localized) + message = message:gsub("{2}", purchase_cost) + message = message:gsub("{3}", gage_coins.amount) + message = message:gsub("{4}", gage_coins.amount - purchase_cost) + + local menuOptions = {} + menuOptions[1] = { + text = managers.localization:text("ModShop_PurchaseWindowAccept"), + callback = ModShop.PurchaseItem + } + menuOptions[2] = { + text = managers.localization:text("ModShop_PurchaseWindowCancel"), + callback = nil, + is_cancel_button = true + } + local window = QuickMenu:new(title, message, menuOptions, true) + +end + +function ModShop:ShowFreeOfCharge() + + local title = managers.localization:text("ModShop_FreeOfChargeTitle") + local message = managers.localization:text("ModShop_FreeOfChargeMessage") + message = message:gsub("{1}", self._purchase_data.name_localized) + local menuOptions = {} + menuOptions[1] = { + text = managers.localization:text("ModShop_FreeOfChargeAccept"), + is_cancel_button = true + } + local window = QuickMenu:new(title, message, menuOptions, true) + +end + +function ModShop:ShowNotEnoughCoins(cost) + + local title = managers.localization:text("ModShop_NotEnoughCoinsWindowTitle") + local message = managers.localization:text("ModShop_NotEnoughCoinsWindowMessage") + message = message:gsub("{1}", self._purchase_data.name_localized) + message = message:gsub("{2}", cost) + local menuOptions = {} + menuOptions[1] = { + text = managers.localization:text("ModShop_NotEnoughCoinsWindowAccept"), + is_cancel_button = true + } + local window = QuickMenu:new(title, message, menuOptions, true) + +end + +function ModShop:PurchaseItem() + + local psuccess, perror = pcall(function() + + if not ExtendedInv then + Print("[Error] Attempting to purchase item with no Extended Inventory...") + return + end + + local purchase_data = ModShop._purchase_data + local item = purchase_data.name + local category = purchase_data.category + local cost = purchase_data.cost + local global_value = purchase_data.global_value + + Print("Purchasing ", item, " from category ", category, " at cost: ", cost, " coins") + + -- Add to weapon inventory + if ModShop:IsWeaponMod(category) then + managers.blackmarket:add_to_inventory(global_value, "weapon_mods", item, true) + ModShop:ReloadBlackMarketAfterPurchase() + end + + -- Add to mask inventory + if ModShop:IsMaskPart(category) then + + managers.blackmarket:add_traded_mask_part_to_inventory(item, category) + + -- Temporary measure to reload mask mods inventory + local blackmarket_gui = managers.menu_component._blackmarket_gui + if blackmarket_gui then + blackmarket_gui:_abort_customized_mask_callback() + end + + end + + -- Add mask to inventory + if ModShop:IsMask(category) then + managers.blackmarket:add_to_inventory(global_value, "masks", item, true) + ModShop:ReloadBlackMarketAfterPurchase() + end + + -- Remove coins + ExtendedInv:TakeItem( ModShop.PurchaseCurrency, cost ) + + end) + if not psuccess then + Print("[Error] " .. perror) + end + +end + +function ModShop:ReloadBlackMarketAfterPurchase() + local blackmarket_gui = managers.menu_component._blackmarket_gui + if blackmarket_gui then + blackmarket_gui:reload() + end +end + +function ModShop:IsWeaponMod(category) + if category == "primaries" or category == "secondaries" then + return true + end + return false +end + +function ModShop:IsMask(category) + if category == "masks" then + return true + end + return false +end + +function ModShop:IsMaskPart(category) + if category == "colors" or category == "textures" or category == "materials" then + return true + end + return false +end diff --git a/GoonMod/mods/disabled/mutators.lua b/GoonMod/mods/disabled/mutators.lua new file mode 100644 index 0000000..73d5b8d --- /dev/null +++ b/GoonMod/mods/disabled/mutators.lua @@ -0,0 +1,857 @@ + +-- Mod Definition +local Mod = class( BaseMod ) +Mod.id = "GameplayMutators" +Mod.Name = "Mutators" +Mod.Desc = "Micro-gameplay mods that give you new gameplay modes and experiences" +Mod.Requirements = {} +Mod.Incompatibilities = {} + +Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() + GoonBase.Mods:RegisterMod( Mod ) +end) + +if not Mod:IsEnabled() then + return +end + +-- Mutators +_G.GoonBase.Mutators = _G.GoonBase.Mutators or {} +local Mutators = _G.GoonBase.Mutators +Mutators.MenuID = "goonbase_mutators_menu" +Mutators.MatchmakingData = "gb_mutators" +Mutators.LoadedMutators = Mutators.LoadedMutators or {} +Mutators.ActiveMutators = Mutators.ActiveMutators or {} +Mutators.ClientMutatorCheck = Mutators.ClientMutatorCheck or {} +Mutators.NetworkTimeoutTime = 3 + +-- Network +Mutators.Network = {} +Mutators.Network.ClearMutators = "ClearMutators" +Mutators.Network.EnableMutator = "EnableMutator" +Mutators.Network.DisableMutator = "DisableMutator" +Mutators.Network.MutatorCheck = "CheckMutator" +Mutators.Network.MutatorCheckSuccess = "CheckMutatorSuccess" +Mutators.Network.MutatorCheckFailure = "CheckMutatorFailure" + +-- Paths +Mutators.MutatorsPath = "/" +Mutators.MutatorsList = { + -- #POPULATE mutators + -- #DEBUG_ONLY + "mutators/base_mutator.lua", + "mutators/mutator_all_cloakers.lua", + "mutators/mutator_all_shields.lua", + "mutators/mutator_all_tazers.lua", + "mutators/mutator_all_bulldozers.lua", + -- "mutators/mutator_all_gangsters.lua", + "mutators/mutator_lightning_speed.lua", + "mutators/mutator_insane_spawnrate.lua", + "mutators/mutator_insane_spawnrate_cops.lua", + "mutators/mutator_suicidal_spawnrate.lua", + "mutators/mutator_suicidal_spawnrate_cops.lua", + "mutators/mutator_shielddozers.lua", + -- "mutators/mutator_tank_cloakers.lua", + "mutators/mutator_realism_mode.lua", + "mutators/mutator_exploding_bullets.lua", + "mutators/mutator_unbreakable.lua", + "mutators/mutator_suicide_cloakers.lua", + "mutators/mutator_instagib.lua", + "mutators/mutator_jamming_weapons.lua", + "mutators/mutator_addicts.lua", + "mutators/mutator_floating_bodies.lua", + "mutators/mutator_no_ammo_pickups.lua", + -- #END +} +Mutators.MenuPrefix = "toggle_mutator_" + +-- Options +if GoonBase.Options.Mutators == nil then + GoonBase.Options.Mutators = {} + GoonBase.Options.Mutators.RandomizerMode = 1 +end + +-- Add mutators menu +Hooks:Add("MenuManagerSetupCustomMenus", "MenuManagerSetupCustomMenus_MutatorsMenu", function(menu_manager, menu_nodes) + + -- Create menu + MenuHelper:NewMenu( Mutators.MenuID ) + + -- Add help button + MenuCallbackHandler.open_mutators_menu_help = function(this, item) + Mutators:ShowHelpMenu() + end + + MenuCallbackHandler.mutators_set_randomizer_mode = function(this, item) + GoonBase.Options.Mutators.RandomizerMode = tonumber(item:value()) + GoonBase.Options:Save() + end + + MenuHelper:AddButton({ + id = "goonbase_mutators_menu_help_button", + title = "Mutators_HelpButton", + desc = "Mutators_HelpButtonDesc", + callback = "open_mutators_menu_help", + menu_id = Mutators.MenuID, + priority = 1003, + }) + + MenuHelper:AddDivider({ + id = "goonbase_mutators_menu_help_divider", + menu_id = Mutators.MenuID, + size = 8, + priority = 1002, + }) + + MenuHelper:AddMultipleChoice({ + id = "goonbase_mutators_menu_randomizer", + title = "Randomizer_Name", + desc = "Randomizer_Desc", + callback = "mutators_set_randomizer_mode", + menu_id = Mutators.MenuID, + priority = 1001, + items = { + [1] = "Randomizer_Off", + [2] = "Randomizer_UpTo1", + [3] = "Randomizer_UpTo2", + [4] = "Randomizer_UpTo3", + [5] = "Randomizer_UpTo4", + [6] = "Randomizer_UpTo5", + }, + value = GoonBase.Options.Mutators.RandomizerMode or 1, + }) + + MenuHelper:AddDivider({ + id = "goonbase_mutators_menu_help_divider2", + menu_id = Mutators.MenuID, + size = 8, + priority = 1000, + }) + + + -- Add mutators to menu + Mutators:AddLoadedMutatorsToMenu() + +end) + +Hooks:Add("MenuManagerBuildCustomMenus", "MenuManagerBuildCustomMenus_MutatorsMenu", function(menu_manager, menu_nodes) + + -- Build menu + local menu_id = Mutators.MenuID + menu_nodes[menu_id] = MenuHelper:BuildMenu( menu_id ) + + -- Add to main menu and lobby only + if menu_nodes.main ~= nil then + MenuHelper:AddMenuItem( menu_nodes.main, menu_id, "Mutators_OptionsName", "Mutators_OptionsDesc", "safehouse", "after" ) + end + if menu_nodes.lobby ~= nil then + MenuHelper:AddMenuItem( menu_nodes.lobby, menu_id, "Mutators_OptionsName", "Mutators_OptionsDesc", "skilltree", "after" ) + end + + -- Add to ingame menu + if menu_nodes.pause ~= nil then + MenuHelper:AddMenuItem( menu_nodes.pause, menu_id, "Mutators_OptionsIngameName", "Mutators_OptionsIngameDesc", "options", "after" ) + end + + -- Verify incompatibilities + Mutators:VerifyAllIncompatibilities() + +end) + +-- Mutators Functions +function Mutators:ShowHelpMenu() + + local title = managers.localization:text("Mutators_HelpTitle") + local message = managers.localization:text("Mutators_HelpMessage") + local menu_options = {} + menu_options[1] = { text = managers.localization:text("Mutators_HelpAccept"), is_cancel_button = true } + local help_menu = QuickMenu:new(title, message, menu_options, true) + +end + +function Mutators:LoadMutators() + + for k, v in pairs( self.MutatorsList ) do + SafeDoFile( GoonBase.Path .. self.MutatorsPath .. v ) + end + +end + +function Mutators:RegisterMutator(mutator) + Print("[Mutators] Registering mutator '" .. mutator:ID() .. "'") + self.LoadedMutators[ mutator:ID() ] = mutator +end + +function Mutators:SetupMutatorsLocalization() + for k, v in pairs( Mutators.LoadedMutators ) do + v:SetupLocalization() + end +end + +function Mutators:SetupMutators() + + if Global.game_settings and Global.game_settings.active_mutators then + + for k, v in pairs( Global.game_settings.active_mutators ) do + if v and Mutators.LoadedMutators[k] then + Mutators.LoadedMutators[k]:Setup() + end + end + + return + else + + for k, v in pairs( Mutators.LoadedMutators ) do + v:Setup() + end + + end + +end + +function Mutators:AddLoadedMutatorsToMenu() + + for k, v in pairs( Mutators.LoadedMutators ) do + if v.HideInOptionsMenu ~= true then + v:SetupMenu() + end + end + +end + +function Mutators:VerifyAllIncompatibilities() + + for i, mutator in pairs( Mutators.LoadedMutators ) do + mutator:VerifyIncompatibilities() + end + +end + +function Mutators:CheckIncompatibilities( mutator ) + + local incompatible_list = mutator:IncompatibleMutators() + local incompatible = {} + local num_incompatibilities = 0 + + -- Find incompatible mutators + for k, v in pairs( Mutators.LoadedMutators ) do + if v:ShouldBeEnabled() and table.contains( incompatible_list, v:ID() ) then + incompatible[k] = true + num_incompatibilities = num_incompatibilities + 1 + end + end + + -- Mutators are all compatible + if num_incompatibilities < 1 then + return true + end + + -- Incompatible mutators + return incompatible + +end + +function Mutators:ShowIncompatibilitiesWindow( mutator, incompatible ) + + -- Display incompatible mutators + local incompatible_str = "" + for k, v in pairs( incompatible ) do + + if incompatible_str ~= "" then + incompatible_str = incompatible_str .. ", " + end + incompatible_str = incompatible_str .. "'" .. Mutators.LoadedMutators[k]:GetLocalizedName() .. "'" + + end + + -- Display + local title = managers.localization:text("Mutators_IncompatibleTitle") + local message = managers.localization:text("Mutators_IncompatibleMessage") + message = message:gsub("{1}", mutator:GetLocalizedName()) + message = message:gsub("{2}", incompatible_str) + local menuOptions = {} + menuOptions[1] = { + text = managers.localization:text("Mutators_IncompatibleAccept"), + is_cancel_button = true + } + local menu = QuickMenu:new(title, message, menuOptions, true) + + return false + +end + +function Mutators:IsRandomizerEnabled() + return (GoonBase.Options.Mutators.RandomizerMode or 1) > 1 +end + +function Mutators:GetNumberOfRandomMutations() + return (GoonBase.Options.Mutators.RandomizerMode or 1) - 1 +end + +function Mutators:AddRandomizedMutations() + + if Mutators:IsRandomizerEnabled() then + + Print("[Mutators] Randomizing mutators, up to ", Mutators:GetNumberOfRandomMutations()) + + if Mutators.ILoadedMutators == nil then + Mutators.ILoadedMutators = {} + for k, v in pairs(Mutators.LoadedMutators) do + table.insert(Mutators.ILoadedMutators, k) + end + end + + local random_mutations = {} + + for i = 1, Mutators:GetNumberOfRandomMutations(), 1 do + + local mutation_id = nil + local attempts = 0 + while mutation_id == nil do + + mutation_id = Mutators.ILoadedMutators[ math.random(1, #Mutators.ILoadedMutators) ] + if random_mutations[mutation_id] or Mutators.ActiveMutators[mutation_id] or not Mutators.LoadedMutators[mutation_id]:VerifyIncompatibilities(true) then + mutation_id = nil + end + + attempts = attempts + 1 + if attempts > 16 then + mutation_id = nil + break + end + + end + + if mutation_id ~= nil then + local mutation = Mutators.LoadedMutators[mutation_id] + if mutation:VerifyIncompatibilities(true) then + mutation:ForceEnable() + end + end + + end + + end + +end + +function Mutators:GetNumberOfActiveMutators() + local i = 0 + for k, v in pairs( Mutators.ActiveMutators ) do + i = i + 1 + end + return i +end + +function Mutators:GetNumberOfMutatorsToBeActive() + + local i = 0 + + for k, v in pairs( Mutators.LoadedMutators ) do + if v:ShouldBeEnabled() then + i = i + 1 + end + end + + if self:IsRandomizerEnabled() then + i = i + self:GetNumberOfRandomMutations() + end + + return i + +end + +function Mutators:PrintActiveMutators() + Print("-----") + Print("Active Mutators:") + for k, v in pairs( Mutators.ActiveMutators ) do + Print(k) + end + Print("-----") +end + +-- Hooks +Hooks:Add("AchievementManagerCheckDisable", "AchievementManagerCheckDisable_Mutators", function(achievement_manager) + + for k, v in pairs( Mutators.LoadedMutators ) do + if v:ShouldBeEnabled() then + achievement_manager:DisableAchievements("mutators") + end + end + + if Mutators:GetNumberOfActiveMutators() > 0 then + achievement_manager:DisableAchievements("mutators") + end + +end) + +-- Load mutators +Hooks:RegisterHook("GoonBaseRegisterMutators") +Hooks:Add("GoonBasePostLoadedMods", "GoonBasePostLoadedMods_Mutators", function() + + Print("[Mutators] Loading Mutators") + Mutators:LoadMutators() + + Hooks:Call("GoonBaseRegisterMutators") + + Print("[Mutators] Setting up mutators") + Mutators:SetupMutatorsLocalization() + Mutators:SetupMutators() + +end) + +-- Permission locking +Hooks:Add("PostCreateCrimenetContractGUI", "PostCreateCrimenetContractGUI_Mutators", function( menu, node, gui ) + + if Mutators:GetNumberOfMutatorsToBeActive() < 1 then + return + end + if not gui._node then + return + end + if not gui._node._items then + return + end + + local items = gui._node._items + for k, v in pairs( items ) do + if v._parameters.name == "lobby_permission" then + local permission = "friends_only" + v:set_value( permission ) + Global.game_settings.permission = permission + end + end + +end) + +Hooks:Add("MenuCallbackHandlerPreChoseLobbyPermission", "MenuCallbackHandlerPreChoseLobbyPermission_Mutators", function( callback_handler, item ) + return Mutators:ForcePrivateGamesForMutators( item ) +end) + +Hooks:Add( "MenuCallbackHandlerPreCrimeNetChoseLobbyPermission", "MenuCallbackHandlerPreCrimeNetChoseLobbyPermission_Mutators", function( callback_handler, item ) + return Mutators:ForcePrivateGamesForMutators( item ) +end ) + +function Mutators:ForcePrivateGamesForMutators( item ) + + if item:value() == "public" and Mutators:GetNumberOfMutatorsToBeActive() > 0 then + item:set_value( "friends_only" ) + if Mutators:IsInGame() then + Mutators:ShowPublicGamesWarningIngame() + return true + end + Mutators:ShowPublicGamesWarningLobby() + return true + end + +end + +function Mutators:ShowPublicGamesWarningLobby() + + local title = managers.localization:text("Mutators_PublicGamesWarning_Title") + local message = managers.localization:text("Mutators_PublicGamesWarning_Message") + local menu_options = {} + menu_options[1] = { text = managers.localization:text("Mutators_PublicGamesWarning_Cancel"), is_cancel_button = true } + local warning = QuickMenu:new(title, message, menu_options, true) + +end + +function Mutators:ShowPublicGamesWarningIngame() + + local title = managers.localization:text("Mutators_PublicGamesWarning_Title") + local message = managers.localization:text("Mutators_PublicGamesWarning_MessageIngame") + local menu_options = {} + menu_options[1] = { text = managers.localization:text("Mutators_PublicGamesWarning_Cancel"), is_cancel_button = true } + local warning = QuickMenu:new(title, message, menu_options, true) + +end + +function Mutators:IsInGame() + if not game_state_machine then + return false + end + return string.find( game_state_machine:current_state_name(), "ingame" ) +end + +-- Network Mutators +Hooks:Add( "MenuCallbackHandlerPreStartTheGame", "MenuCallbackHandlerPreStartTheGame_Mutators", function( callback_handler ) + + Mutators.ActiveMutators = {} + for k, v in pairs( Mutators.LoadedMutators ) do + if v and v:ShouldBeEnabled() then + Mutators.ActiveMutators[k] = true + end + end + + if not GoonBase.Network:IsMultiplayer() or ( GoonBase.Network:IsMultiplayer() and GoonBase.Network:IsHost() ) then + Mutators:AddRandomizedMutations() + end + + if Global.game_settings then + + Global.game_settings.active_mutators = {} + for k, v in pairs( Mutators.ActiveMutators ) do + Global.game_settings.active_mutators[k] = v + end + + end + + if Mutators:CheckNetworkMutators(callback_handler) then + + callback_handler:delay_game_start( "MutatorNetworking" ) + + Mutators._game_delay_time = Application:time() + Mutators.ClientMutatorCheck = {} + + Mutators:ClearClientsNetworkedMutators() + Mutators:ShowNetworkingMutatorsWindow() + + for k, v in pairs( Mutators.ActiveMutators ) do + Mutators.ClientMutatorCheck[k] = {} + Mutators:SendNetworkedMutatorToClients( k, true ) + Mutators:CheckIfClientsHaveMutator( k ) + end + + return true + + end + +end ) + +function Mutators:CheckNetworkMutators( callback_handler ) + if Global.game_settings and not Global.game_settings.single_player then + if GoonBase.Network:IsMultiplayer() and GoonBase.Network:IsHost() and GoonBase.Network:GetNumberOfPeers() > 0 then + return true + end + end + return false +end + +function Mutators:ShowNetworkingMutatorsWindow() + + local title = managers.localization:text("NetworkedMutators_SendingData_Title") + local message = managers.localization:text("NetworkedMutators_SendingData_Message") + local menuOptions = {} + menuOptions[1] = { + text = managers.localization:text("NetworkedMutators_SendingData_Cancel"), + callback = Mutators.NetworkingMutatorsCancel, + is_cancel_button = true + } + -- #DEBUG_ONLY + menuOptions[2] = { + text = managers.localization:text("NetworkedMutators_SendingData_DebugForce"), + callback = Mutators.DebugForceStartGame, + } + menuOptions[3] = { + text = managers.localization:text("NetworkedMutators_SendingData_DebugRelease"), + callback = Mutators.DebugReleaseStartDelay, + } + -- #END + local menu = QuickMenu:new(title, message, menuOptions) + menu.dialog_data.indicator = true + menu:Show() + + self._open_window = menu + +end + +function Mutators:DebugForceStartGame() + MenuCallbackHandler:_start_the_game() +end + +function Mutators:DebugReleaseStartDelay() + MenuCallbackHandler:release_game_start_delay( "MutatorNetworking" ) +end + +function Mutators:NetworkingMutatorsCancel() + MenuCallbackHandler:release_game_start_delay( "MutatorNetworking" ) + MenuCallbackHandler._delayed_start_game = false +end + +-- Matchmaking +Hooks:Add("NetworkMatchmakingSetAttributes", "NetworkMatchmakingSetAttributes_" .. Mod:ID(), function(matchmaking, settings) + + local mutator_data = "" + for k, v in pairs( Mutators.ActiveMutators ) do + mutator_data = mutator_data .. k .. "/" + end + if matchmaking and matchmaking._lobby_attributes then + matchmaking._lobby_attributes[ Mutators.MatchmakingData ] = mutator_data + end + +end) + +Hooks:Add("NetworkMatchmakingJoinOKServer", "NetworkMatchmakingJoinOKServer_" .. Mod:ID(), function(matchmaking, room_id, skip_showing_dialog) + + local lobby = Steam:lobby(room_id) + local mutator_key = lobby:key_value( Mutators.MatchmakingData ) + if mutator_key == "value_missing" or mutator_key == "value_pending" then + if Global.game_settings and Global.game_settings.active_mutators then + Global.game_settings.active_mutators = {} + end + return + end + + if Global.game_settings then + Global.game_settings.active_mutators = {} + end + + local mutators_data = string.split( mutator_key, "[/]" ) + for k, v in pairs( mutators_data ) do + if not string.is_nil_or_empty(v) then + Global.game_settings.active_mutators[v] = true + end + end + + Hooks:PreHook( ClientNetworkSession, "on_join_request_cancelled", "NetworkSessionOnJoinRequestCancelled_" .. Mod:ID(), function() + if Global.game_settings then + Global.game_settings.active_mutators = {} + end + end ) + +end) + +-- Network Messages +Hooks:Add("NetworkReceivedData", "NetworkReceivedData_" .. Mod:ID(), function(sender, messageType, data) + + if messageType == Mutators.Network.ClearMutators then + Mutators:ClearNetworkedMutators() + end + + if messageType == Mutators.Network.EnableMutator then + Mutators:SetNetworkedMutator( data, true ) + end + + if messageType == Mutators.Network.DisableMutator then + Mutators:SetNetworkedMutator( data, false ) + end + + if messageType == Mutators.Network.MutatorCheck then + Mutators:CheckIfHasMutator( sender, data ) + end + + if messageType == Mutators.Network.MutatorCheckSuccess then + Mutators:MarkClientHasMutator( sender, data, true ) + end + + if messageType == Mutators.Network.MutatorCheckFailure then + Mutators:MarkClientHasMutator( sender, data, false ) + end + +end) + +function Mutators:ClearClientsNetworkedMutators() + GoonBase.Network:SendToPeers( Mutators.Network.ClearMutators, "" ) +end + +function Mutators:ClearNetworkedMutators() + if Global.game_settings then + Global.game_settings.active_mutators = {} + end +end + +function Mutators:SendNetworkedMutatorToClients( mutator_id, enabled ) + if enabled then + GoonBase.Network:SendToPeers( Mutators.Network.EnableMutator, mutator_id ) + else + GoonBase.Network:SendToPeers( Mutators.Network.DisableMutator, mutator_id ) + end +end + +function Mutators:SetNetworkedMutator( mutator_id, enable ) + if Global.game_settings then + if not Global.game_settings.active_mutators then + Global.game_settings.active_mutators = {} + end + Global.game_settings.active_mutators[mutator_id] = enable + end +end + +function Mutators:CheckIfClientsHaveMutator( mutator_id ) + GoonBase.Network:SendToPeers( Mutators.Network.MutatorCheck, mutator_id ) +end + +function Mutators:CheckIfHasMutator( sender, mutator_id ) + + if Mutators.LoadedMutators[ mutator_id ] == nil then + GoonBase.Network:SendToPeer( sender, Mutators.Network.MutatorCheckFailure, mutator_id ) + else + GoonBase.Network:SendToPeer( sender, Mutators.Network.MutatorCheckSuccess, mutator_id ) + end + +end + +function Mutators:MarkClientHasMutator( client, mutator_id, has_mutator ) + + if not Mutators.ClientMutatorCheck then + Mutators.ClientMutatorCheck = {} + end + + if not Mutators.ClientMutatorCheck[ mutator_id ] then + Mutators.ClientMutatorCheck[ mutator_id ] = {} + end + + Mutators.ClientMutatorCheck[ mutator_id ][ client ] = has_mutator + +end + +Hooks:Add("MenuUpdate", "MenuUpdate_" .. Mod:ID(), function(t, dt) + Mutators:CheckMutatorTimeout() +end) + +Hooks:Add("GameSetupUpdate", "GameSetupUpdate_" .. Mod:ID(), function(t, dt) + Mutators:CheckMutatorTimeout() +end) + +function Mutators:CheckMutatorTimeout() + + if self._game_delay_time then + local t = Application:time() - self._game_delay_time + if t > self.NetworkTimeoutTime then + self:CheckAllClientsHaveMutators() + self._game_delay_time = nil + end + end + +end + +function Mutators:CheckAllClientsHaveMutators() + + if not self.ClientMutatorCheck then + return + end + + local missing_mutator = false + local missing_mutator_text = "" + local added_missing_mutator_text = "" + for mutator_id, mutator in pairs( self.ClientMutatorCheck ) do + + if Mutators.LoadedMutators[mutator_id] then + + added_missing_mutator_text = Mutators.LoadedMutators[mutator_id]:GetLocalizedName() + for client_id, has_mutator in pairs( mutator ) do + + if not has_mutator then + + missing_mutator = true + + if not string.is_nil_or_empty( added_missing_mutator_text ) then + missing_mutator_text = missing_mutator_text .. "\n" .. added_missing_mutator_text .. ": " + missing_mutator_text = missing_mutator_text .. GoonBase.Network:GetNameFromPeerID(client_id) + added_missing_mutator_text = "" + else + missing_mutator_text = missing_mutator_text .. ", " .. GoonBase.Network:GetNameFromPeerID(client_id) + end + + end + + end + for k, v in pairs( GoonBase.Network:GetPeers() ) do + local client_id = v:id() + if not mutator[ client_id ] then + + missing_mutator = true + + if not string.is_nil_or_empty( added_missing_mutator_text ) then + missing_mutator_text = missing_mutator_text .. "\n" .. added_missing_mutator_text .. ": " + missing_mutator_text = missing_mutator_text .. GoonBase.Network:GetNameFromPeerID(client_id) + added_missing_mutator_text = "" + else + missing_mutator_text = missing_mutator_text .. ", " .. GoonBase.Network:GetNameFromPeerID(client_id) + end + + end + end + + end + + end + + if missing_mutator then + + managers.system_menu:close( self._open_window.dialog_data.id ) + + local title = managers.localization:text("MissingMutators_Title") + local message = managers.localization:text("MissingMutators_Message") + if not string.is_nil_or_empty( missing_mutator_text ) then + message = message .. missing_mutator_text + end + local menuOptions = {} + menuOptions[1] = { + text = managers.localization:text("MissingMutators_Continue"), + callback = Mutators.ContinueStartGame, + } + menuOptions[2] = { + text = managers.localization:text("MissingMutators_Cancel"), + callback = Mutators.NetworkingMutatorsCancel, + is_cancel_button = true + } + local menu = QuickMenu:new(title, message, menuOptions, true) + self._open_window = menu + + else + Mutators:ContinueStartGame() + end + +end + +function Mutators.ContinueStartGame() + managers.system_menu:close( Mutators._open_window.dialog_data.id ) + Mutators._open_window = nil + MenuCallbackHandler:release_game_start_delay( "MutatorNetworking" ) + if Mutators:IsInGame() then + MenuCallbackHandler:_start_the_game() + end +end + +-- Mission Briefing Screen +Hooks:Add("MissionBriefingGUIPostInit", "MissionBriefingGUIPostInit_" .. Mod:ID(), function(self, saferect_ws, fullrect_ws, node) + + if Mutators:GetNumberOfActiveMutators() > 0 then + self._mutators_item = MutatorsItem:new(self._panel, utf8.to_upper(managers.localization:text("menu_mutators")), Global.game_settings.single_player and 5 or 6) + table.insert(self._items, self._mutators_item) + end + +end) + +MutatorsItem = {} +Hooks:Add("MissionBriefingGUIPreInit", "MissionBriefingGUIPreInit_" .. Mod:ID(), function(self, saferect_ws, fullrect_ws, node) + + MutatorsItem = class( DescriptionItem ) + MutatorsItem.init = function(self, panel, text, i, saved_descriptions) + + MutatorsItem.super.init(self, panel, text, i) + if not managers.job:has_active_job() then + return + end + + local title_text = self._panel:child("title_text") + if title_text then + title_text:set_w( self._panel:w() ) + title_text:set_text( managers.localization:to_upper_text("BriefingMenu_ActiveMutators") ) + end + + local pro_text = self._panel:child("pro_text") + if pro_text then + pro_text:set_text("") + end + + local description_text = self._scroll_panel:child("description_text") + if description_text then + + local mutators_str = "" + for k, v in pairs( Mutators.ActiveMutators ) do + + local mutation = Mutators.LoadedMutators[k] + if mutation then + mutators_str = mutators_str .. mutation:GetLocalizedName() .. ": " .. mutation:GetLocalizedDesc(true) .. "\n" + end + + end + self:set_description_text( mutators_str ) + + end + + end + +end) diff --git a/GoonMod/mods/disabled/push_to_interact.lua b/GoonMod/mods/disabled/push_to_interact.lua new file mode 100644 index 0000000..ae80d60 --- /dev/null +++ b/GoonMod/mods/disabled/push_to_interact.lua @@ -0,0 +1,231 @@ + +local interact_menu_id = "goonbase_pushtointeract_menu" + +-- Mod Definition +local Mod = class( BaseMod ) +Mod.id = "PushToInteract" +Mod.Name = "Push to Interact" +Mod.Desc = "Push interact key to toggle interacting with an object" +Mod.Requirements = {} +Mod.Incompatibilities = {} + +Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() + GoonBase.Mods:RegisterMod( Mod ) +end) + +if not Mod:IsEnabled() then + return +end + +-- Push to Interact +_G.GoonBase.PushToInteract = _G.GoonBase.PushToInteract or {} +local PushToInteract = _G.GoonBase.PushToInteract +PushToInteract.ForceKeepInteraction = { + ["corpse_alarm_pager"] = true, + ["c4_diffusible"] = true, + ["disarm_bomb"] = true, +} + +-- Options +if not GoonBase.Options.PushToInteract then + GoonBase.Options.PushToInteract = {} + GoonBase.Options.PushToInteract.Enabled = true + GoonBase.Options.PushToInteract.GraceTime = 0.2 + GoonBase.Options.PushToInteract.ShowHelper = false +end + +-- Functions +function PushToInteract:CreateInteractionIndicator() + + if not self._workspace and GoonBase.Options.PushToInteract.ShowHelper then + + self._workspace = Overlay:newgui():create_screen_workspace(0, 0, 1, 1) + self._interaction_radius = self._workspace:panel():w() / 12 + self._interaction_circle = CircleBitmapGuiObject:new(self._workspace:panel(), { + use_bg = false, + radius = self._interaction_radius, + sides = 64, + current = 64, + total = 64, + color = Color.white:with_alpha(1), + blend_mode = "add", + image = "guis/textures/pd2/specialization/progress_ring", + layer = 2, + x = self._workspace:panel():w() / 2 - self._interaction_radius - 0.5, + y = self._workspace:panel():h() / 2 - self._interaction_radius - 0.5, + }) + self._interaction_circle:set_current( 0 ) + + end + +end + +function PushToInteract:DestroyWorkspace() + + if self._workspace and alive(self._workspace) then + Overlay:newgui():destroy_workspace(self._workspace) + self._workspace = nil + end + +end + +function PushToInteract:ShowInteractionHelper() + if not GoonBase.Options.PushToInteract.ShowHelper then + return + end + if not self._interaction_circle then + PushToInteract:CreateInteractionIndicator() + end + self._interaction_circle:set_current( 1 ) +end + +function PushToInteract:UpdateInteractionHelper(t) + if not GoonBase.Options.PushToInteract.ShowHelper then + return + end + if not self._interaction_circle then + PushToInteract:CreateInteractionIndicator() + end + self._interaction_circle:set_current( t ) +end + +function PushToInteract:HideInteractionHelper() + if not GoonBase.Options.PushToInteract.ShowHelper then + return + end + if not self._interaction_circle then + PushToInteract:CreateInteractionIndicator() + end + self._interaction_circle:set_current( 0 ) +end + +-- Hooks +Hooks:Add("PlayerStandardCheckActionInteract", "PlayerStandardCheckActionInteract_PushToInteract", function(ply, t, input) + + if not GoonBase.Options.PushToInteract.Enabled then + return + end + + local grace_time = (GoonBase.Options.PushToInteract.GraceTime or 0.2) + ply._last_interact_press_t = ply._last_interact_press_t or 0 + + if input.btn_interact_press then + + ply._last_interact_press_t = t + + if ply:_interacting() then + ply:_interupt_action_interact() + PushToInteract:HideInteractionHelper() + return false + end + + elseif input.btn_interact_release then + + local dt = t - ply._last_interact_press_t + local always_use = grace_time < 0.001 + + if managers.interaction and alive( managers.interaction:active_object() ) then + local tw = managers.interaction:active_object():interaction().tweak_data + if PushToInteract.ForceKeepInteraction[tw] then + always_use = true + end + end + + if always_use or dt >= grace_time then + return false + end + + end + + if ply._last_interact_press_t and ply:_interacting() then + local dt = t - ply._last_interact_press_t + if dt >= grace_time then + + if ply._interact_expire_t then + local x = (t - ply._last_interact_press_t) / (ply._interact_expire_t - ply._last_interact_press_t) + PushToInteract:UpdateInteractionHelper( x ) + else + PushToInteract:ShowInteractionHelper() + end + + else + PushToInteract:HideInteractionHelper() + end + else + PushToInteract:HideInteractionHelper() + end + +end) + +-- Menu +Hooks:Add("MenuManagerSetupCustomMenus", "MenuManagerSetupCustomMenus_PushToInteract", function( menu_manager, menu_nodes ) + MenuHelper:NewMenu( interact_menu_id ) +end) + +Hooks:Add("MenuManagerSetupGoonBaseMenu", "MenuManagerSetupGoonBaseMenu_PushToInteract", function( menu_manager ) + + -- Add corpse mod menu button + MenuHelper:AddButton({ + id = "pushtointeract_mod_menu_button", + title = "OptionsMenu_PushInteractSubmenuTitle", + desc = "OptionsMenu_PushInteractSubmenuDesc", + next_node = interact_menu_id, + menu_id = "goonbase_options_menu" + }) + + -- Callbacks + MenuCallbackHandler.toggle_pushtointeract = function(this, item) + GoonBase.Options.PushToInteract.Enabled = item:value() == "on" and true or false + GoonBase.Options:Save() + end + + MenuCallbackHandler.set_pushtointeract_grace_period = function(this, item) + GoonBase.Options.PushToInteract.GraceTime = tonumber( item:value() ) + GoonBase.Options:Save() + end + + MenuCallbackHandler.toggle_pushtointeract_helper = function(this, item) + GoonBase.Options.PushToInteract.ShowHelper = item:value() == "on" and true or false + GoonBase.Options:Save() + end + + -- Menu + MenuHelper:AddToggle({ + id = "pushtointeract_toggle", + title = "OptionsMenu_PushInteractEnableTitle", + desc = "OptionsMenu_PushInteractEnableDesc", + callback = "toggle_pushtointeract", + value = GoonBase.Options.PushToInteract.Enabled, + menu_id = interact_menu_id, + priority = 50 + }) + + MenuHelper:AddSlider({ + id = "pushtointeract_timer_slider", + title = "OptionsMenu_PushInteractTimeTitle", + desc = "OptionsMenu_PushInteractTimeDesc", + callback = "set_pushtointeract_grace_period", + value = GoonBase.Options.PushToInteract.GraceTime, + min = 0, + max = 2, + step = 0.01, + show_value = true, + menu_id = interact_menu_id, + priority = 49 + }) + + MenuHelper:AddToggle({ + id = "pushtointeract_toggle_showhelper", + title = "OptionsMenu_PushInteractHelperTitle", + desc = "OptionsMenu_PushInteractHelperDesc", + callback = "toggle_pushtointeract_helper", + value = GoonBase.Options.PushToInteract.ShowHelper or false, + menu_id = interact_menu_id, + priority = 48 + }) + +end) + +Hooks:Add("MenuManagerBuildCustomMenus", "MenuManagerBuildCustomMenus_PushToInteract", function( menu_manager, mainmenu_nodes ) + mainmenu_nodes[interact_menu_id] = MenuHelper:BuildMenu( interact_menu_id ) +end) diff --git a/GoonMod/mods/disabled/stat_trak.lua b/GoonMod/mods/disabled/stat_trak.lua new file mode 100644 index 0000000..bf171a6 --- /dev/null +++ b/GoonMod/mods/disabled/stat_trak.lua @@ -0,0 +1,372 @@ + +-- Mod Definition +local Mod = class( BaseMod ) +Mod.id = "StatTrakWeapons" +Mod.Name = "Stat-trak Weapons" +Mod.Desc = "" +Mod.Requirements = {} +Mod.Incompatibilities = {} +Mod.EnabledByDefault = true + +Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod:ID(), function() + GoonBase.Mods:RegisterMod( Mod ) +end) + +if not Mod:IsEnabled() then + return +end + +-- Stat-trak +_G.GoonBase.StatTrak = _G.GoonBase.StatTrak or {} +local StatTrak = _G.GoonBase.StatTrak +StatTrak.MenuId = "goonbase_stattrak_menu" +StatTrak.InspectWeaponKey = "StattrakInspectWeapon" +StatTrak.CycleStatKey = "StattrakCycleMode" +StatTrak.CustomKeys = { + INSPECT = GoonBase.Options.StatTrak ~= nil and GoonBase.Options.StatTrak.InspectWeaponKey or "", + CYCLE_MODE = GoonBase.Options.StatTrak ~= nil and GoonBase.Options.StatTrak.CycleStatKey or "" +} + +StatTrak.CurrentMode = 1 +StatTrak.Modes = { + [1] = "all", + [2] = "bulldozers", + [3] = "shields", + [4] = "tasers", + [5] = "snipers", + [6] = "cloakers", +} + +StatTrak.ModesFriendlyText = { + ["all"] = "", + ["bulldozers"] = "Bulldozers", + ["shields"] = "Shields", + ["tasers"] = "Tasers", + ["snipers"] = "Snipers", + ["cloakers"] = "Cloakers", +} + +StatTrak._example_kills = { + ["all"] = 136, + ["bulldozers"] = 1, + ["shields"] = 12, + ["tasers"] = 7, + ["snipers"] = 4, + ["cloakers"] = 0, +} + +-- Load Weapon Offsets +StatTrak.WeaponOffsetsFile = "mods/stat_trak_weapon_offsets.lua" +SafeDoFile( GoonBase.Path .. StatTrak.WeaponOffsetsFile ) + +-- Options +if GoonBase.Options.StatTrak == nil then + GoonBase.Options.StatTrak = {} + GoonBase.Options.StatTrak.InspectWeaponKey = "" + GoonBase.Options.StatTrak.CycleStatKey = "" +end + +-- Menu +Hooks:Add("MenuManagerSetupCustomMenus", "MenuManagerSetupCustomMenus_" .. Mod:ID(), function(menu_manager, menu_nodes) + MenuHelper:NewMenu( StatTrak.MenuId ) +end) + +Hooks:Add("MenuManagerSetupGoonBaseMenu", "MenuManagerSetupGoonBaseMenu_" .. Mod:ID(), function( menu_manager ) + + -- Add corpse mod menu button + MenuHelper:AddButton({ + id = "stattrak_mod_menu_button", + title = "OptionsMenu_StatTrakSubmenuTitle", + desc = "OptionsMenu_StatTrakSubmenuDesc", + next_node = StatTrak.MenuId, + menu_id = "goonbase_options_menu" + }) + + MenuHelper:AddKeybinding({ + id = "keybind_stattrak_inspect", + title = managers.localization:text("OptionsMenu_InspectWeapon"), + desc = managers.localization:text("OptionsMenu_InspectWeaponDesc"), + connection_name = StatTrak.InspectWeaponKey, + button = StatTrak.CustomKeys.INSPECT, + binding = StatTrak.CustomKeys.INSPECT, + menu_id = StatTrak.MenuId, + priority = 20 + }) + + MenuHelper:AddKeybinding({ + id = "keybind_stattrak_cycle", + title = managers.localization:text("OptionsMenu_CycleStattrakMode"), + desc = managers.localization:text("OptionsMenu_CycleStattrakModeDesc"), + connection_name = StatTrak.CycleStatKey, + button = StatTrak.CustomKeys.CYCLE_MODE, + binding = StatTrak.CustomKeys.CYCLE_MODE, + menu_id = StatTrak.MenuId, + priority = 19 + }) + +end) + +Hooks:Add("MenuManagerBuildCustomMenus", "MenuManagerBuildCustomMenus_" .. Mod:ID(), function(menu_manager, mainmenu_nodes) + mainmenu_nodes[StatTrak.MenuId] = MenuHelper:BuildMenu( StatTrak.MenuId ) +end) + +-- Custom keybinds +Hooks:Add("CustomizeControllerOnKeySet", "CustomizeControllerOnKeySet_" .. Mod:ID(), function(item) + + local psuccess, perror = pcall(function() + + if item._name == StatTrak.InspectWeaponKey then + StatTrak.CustomKeys.INSPECT = item._input_name_list[1] + StatTrak:SaveBindings() + end + + if item._name == StatTrak.CycleStatKey then + StatTrak.CustomKeys.CYCLE_MODE = item._input_name_list[1] + StatTrak:SaveBindings() + end + + end) + if not psuccess then + Print("[Error] " .. perror) + end + +end) + +function StatTrak:SaveBindings() + GoonBase.Options.StatTrak.InspectWeaponKey = StatTrak.CustomKeys.INSPECT + GoonBase.Options.StatTrak.CycleStatKey = StatTrak.CustomKeys.CYCLE_MODE + GoonBase.Options:Save() +end + +Hooks:Add("GameSetupUpdate", "GameSetupUpdate_" .. Mod:ID(), function(t, dt) + StatTrak:UpdateBindings() +end) + +function StatTrak:UpdateBindings() + + if self._input == nil then + self._input = Input:keyboard() + end + if managers.hud:chat_focus() then + return + end + + local inspect_key = StatTrak.CustomKeys.INSPECT + if not string.is_nil_or_empty(inspect_key) then + if self._input:pressed(Idstring(inspect_key)) then + StatTrak._inspecting = true + local state = managers.player:local_player():movement():current_state() + state:_stance_entered() + end + if self._input:released(Idstring(inspect_key)) then + StatTrak._inspecting = false + local state = managers.player:local_player():movement():current_state() + state:_stance_entered() + end + end + + local cycle_key = StatTrak.CustomKeys.CYCLE_MODE + if not string.is_nil_or_empty(cycle_key) and self._input:pressed(Idstring(cycle_key)) then + StatTrak:CycleMode() + end + +end + +function StatTrak:GetCurrentModeName() + return StatTrak.Modes[StatTrak.CurrentMode] +end + +function StatTrak:CycleMode() + + StatTrak.CurrentMode = StatTrak.CurrentMode + 1 + if StatTrak.CurrentMode > #StatTrak.Modes then + StatTrak.CurrentMode = 1 + end + +end + +Hooks:Add("FPCameraBaseStanceEnteredCallback", "FPCameraBaseStanceEnteredCallback_" .. Mod:ID(), function(camera, new_shoulder_stance, new_head_stance, new_vel_overshot, new_fov, new_shakers, stance_mod, duration_multiplier, duration) + + local psuccess, perror = pcall(function() + + if StatTrak._inspecting then + + -- local factory_id = managers.blackmarket:equipped_primary().factory_id + local factory_id = managers.player:local_player():inventory():equipped_unit():base()._factory_id + PrintStr(factory_id) + if factory_id ~= nil then + + local default_offset = StatTrak.StandardOffsets + local weapon_offset = StatTrak.WeaponOffsets[ factory_id ] + + stance_mod.rotation = weapon_offset.inspect_rotation_offset or default_offset.inspect_rotation_offset + stance_mod.translation = weapon_offset.inspect_position_offset or default_offset.inspect_position_offset + + end + + end + + end) + if not psuccess then + Print("[Error] " .. perror) + end + +end) + +function StatTrak.add_kill_tracker(weapon, unit) + + local psuccess, perror = pcall(function() + + local self = weapon + self._unit:set_extension_update_enabled( Idstring("base"), true ) + + local gui_object = unit:get_object( Idstring("a_body") ) + if gui_object == nil then + return + end + + self._gui_object = gui_object + self._ws_params = StatTrak:GetDefaultOffsets() + + local w = self._ws_params.w + local h = self._ws_params.h + local scaled_w, scaled_h = self._ws_params.w * self._ws_params.scale, self._ws_params.h * self._ws_params.scale + + local rot = gui_object:rotation() * self._ws_params.rotation_offset( gui_object ) + local pos = gui_object:position() + self._ws_params.position_offset( gui_object ) + + local new_gui = World:newgui() + local ws = new_gui:create_world_workspace(w, h, pos, Vector3(scaled_w, 0, 0):rotate_with(rot), Vector3(0, 0, -scaled_h):rotate_with(rot)) + self._ws = ws + + ws:panel():clear() + ws:panel():set_alpha(0.8) + ws:panel():rect({ + color = Color.black, + layer = -1 + }) + self._back_drop_gui = MenuBackdropGUI:new(ws) + local panel = self._back_drop_gui:get_new_background_layer() + local default_offset = 112 + local font_size = 220 + self._kills_display = panel:text({ + text = "1234", + y = self._ws:panel():h() / 2 - default_offset, + font = tweak_data.menu.pd2_large_font, + align = "center", + vertical = "center", + font_size = font_size, + layer = 0, + visible = true, + color = OffshoreGui.MONEY_COLOR + }) + + local font_size = 80 + local default_offset = 40 + self._mode_display = panel:text({ + text = "", + y = self._ws:panel():h() / 2 - default_offset, + font = tweak_data.menu.pd2_large_font, + align = "right", + vertical = "center", + font_size = font_size, + layer = 0, + visible = true, + color = TextGui.COLORS.light_red:with_alpha(0.6) + }) + + end) + if not psuccess then + Print("[Error] " .. perror) + end + +end + +Hooks:Add("NewRaycastWeaponBaseInit", "NewRaycastWeaponBaseInit_" .. Mod:ID(), function( weapon, unit ) + + if NewRaycastWeaponBase.add_kill_tracker == nil then + NewRaycastWeaponBase.add_kill_tracker = StatTrak.add_kill_tracker + end + + weapon:add_kill_tracker(unit) + +end) + +function StatTrak.update_kill_tracker_offsets(weapon, factory_id) + weapon._ws_params = StatTrak:GetOffsetsForWeaponID(factory_id) +end + +Hooks:Add("NewRaycastWeaponBaseSetFactoryData", "NewRaycastWeaponBaseSetFactoryData_" .. Mod:ID(), function(weapon, factory_id) + + if NewRaycastWeaponBase.update_kill_tracker_offsets == nil then + NewRaycastWeaponBase.update_kill_tracker_offsets = StatTrak.update_kill_tracker_offsets + end + + weapon:update_kill_tracker_offsets(factory_id) + +end) + +function StatTrak.update_kill_tracker_text(weapon, unit, t, dt) + + if not StatTrak.DefaultKillText then + StatTrak.DefaultKillText = managers.localization:get_default_macro("BTN_SKULL") .. " {0}" + end + + local psuccess, perror = pcall(function() + + if weapon._kills_display then + local kills = StatTrak._example_kills[ StatTrak:GetCurrentModeName() ] or 1024 + weapon._kills_display:set_text( StatTrak.DefaultKillText:gsub("{0}", tostring(kills)) ) + end + if weapon._mode_display then + weapon._mode_display:set_text( StatTrak.ModesFriendlyText[ StatTrak:GetCurrentModeName() ] ) + end + + end) + if not psuccess then + Print("[Error] " .. perror) + end + +end + +function StatTrak.update_kill_tracker_position(weapon, unit, t, dt) + + local psuccess, perror = pcall(function() + + local self = weapon + if self._gui_object and self._ws then + + local ws = self._ws + local gui_object = self._gui_object + + local w = self._ws_params.w + local h = self._ws_params.h + local scaled_w, scaled_h = self._ws_params.w * self._ws_params.scale, self._ws_params.h * self._ws_params.scale + + local pos = gui_object:position() + self._ws_params.position_offset( gui_object ) + local rot = gui_object:rotation() * self._ws_params.rotation_offset( gui_object ) + + ws:set_world( w, h, pos, Vector3(scaled_w, 0, 0):rotate_with(rot), Vector3(0, 0, -scaled_h):rotate_with(rot) ) + + end + + end) + if not psuccess then + Print("[Error] " .. perror) + end + +end + +Hooks:Add("NewRaycastWeaponBaseUpdate", "NewRaycastWeaponBaseUpdate_" .. Mod:ID(), function(weapon, unit, t, dt) + + if NewRaycastWeaponBase.update_kill_tracker_text == nil then + NewRaycastWeaponBase.update_kill_tracker_text = StatTrak.update_kill_tracker_text + end + if NewRaycastWeaponBase.update_kill_tracker_position == nil then + NewRaycastWeaponBase.update_kill_tracker_position = StatTrak.update_kill_tracker_position + end + + weapon:update_kill_tracker_text(unit, t, dt) + weapon:update_kill_tracker_position(unit, t, dt) + +end) diff --git a/GoonMod/mods/disabled/stat_trak_weapon_offsets.lua b/GoonMod/mods/disabled/stat_trak_weapon_offsets.lua new file mode 100644 index 0000000..502b925 --- /dev/null +++ b/GoonMod/mods/disabled/stat_trak_weapon_offsets.lua @@ -0,0 +1,62 @@ + +local StatTrak = _G.GoonBase.StatTrak + +function StatTrak:GetDefaultOffsets() + return clone( StatTrak.StandardOffsets ) +end + +function StatTrak:GetOffsetsForWeaponID(factory_id) + + local offsets = self:GetDefaultOffsets() + local weapon_offsets = StatTrak.WeaponOffsets[ factory_id ] + if weapon_offsets == nil then + Print("[Warning] Weapon factory ID '", tostring(factory_id) ,"' does not exist in weapon offsets table. Using default offsets.") + return offsets + end + + offsets.w = weapon_offsets.w or offsets.w + offsets.h = weapon_offsets.h or offsets.h + offsets.scale = weapon_offsets.scale or offsets.scale + offsets.position_offset = weapon_offsets.position_offset or offsets.position_offset + offsets.rotation_offset = weapon_offsets.rotation_offset or offsets.rotation_offset + offsets.inspect_position_offset = weapon_offsets.inspect_position_offset or offsets.inspect_position_offset + offsets.inspect_rotation_offset = weapon_offsets.inspect_rotation_offset or offsets.inspect_rotation_offset + + return offsets + +end + +StatTrak.StandardOffsets = { + w = 960, + h = 240, + scale = 0.01, + position_offset = function(parent) + local rot = parent:rotation() + return (rot:x() * 0) + (rot:y() * 0) + (rot:z() * 0) + end, + rotation_offset = function(parent) + return Rotation(0, 0, 0) + end, + inspect_position_offset = Vector3(40, 10, 0), + inspect_rotation_offset = Rotation(80, 10, 45), +} + +StatTrak.WeaponOffsets = { + + ["wpn_fps_pis_1911"] = { + position_offset = function(parent) + local rot = parent:rotation() + return (rot:x() * -1.5) + (rot:y() * 14) + (rot:z() * 7.5) + end, + rotation_offset = function(parent) + return Rotation(-90, 0, 0) + end, + inspect_position_offset = Vector3(40, 10, 0), + inspect_rotation_offset = Rotation(80, 10, 45), + }, + + ["wpn_fps_snp_r93"] = { + + }, + +} diff --git a/GoonMod/mods/disabled/trading.lua b/GoonMod/mods/disabled/trading.lua new file mode 100644 index 0000000..4d19b9b --- /dev/null +++ b/GoonMod/mods/disabled/trading.lua @@ -0,0 +1,1497 @@ + +-- Mod Definition +local Mod = class( BaseMod ) +Mod.id = "Trading" +Mod.Name = "Crime.net Cargo" +Mod.Desc = "Send and receive weapons, weapon mods, masks, and mask parts between your friends and other players" +Mod.Requirements = {} +Mod.Incompatibilities = {} + +Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() + GoonBase.Mods:RegisterMod( Mod ) +end) + +if not Mod:IsEnabled() then + return +end + +-- Trading +_G.GoonBase.Trading = _G.GoonBase.Trading or {} +local Trading = _G.GoonBase.Trading + +Trading.TradablePeers = Trading.TradablePeers or {} +Trading.BlackMarketGUI = nil +Trading.TradeData = {} +Trading.ActiveTradeWindow = nil +Trading.MenuId = "goonbase_trading_options_menu" + +Trading.BlacklistedMasks = { + ["character_locked"] = true, +} + +-- Categories +Trading.Categories = {} +Trading.Categories.PrimaryWeapon = "primaries" +Trading.Categories.SecondaryWeapon = "secondaries" +Trading.Categories.WeaponMod = "weapon_mod" +Trading.Categories.Mask = "masks" +Trading.Categories.MaskColour = "colors" +Trading.Categories.MaskPattern = "textures" +Trading.Categories.MaskMaterial = "materials" + +-- Network +Trading.Network = Trading.Network or {} +Trading.Network.MessageType = Trading.Network.MessageType or {} +Trading.Network.MessageType.System = "TradeSystem" +Trading.Network.MessageType.Request = "TradeRequest" +Trading.Network.MessageType.RequestResponse = "TradeRequestResponse" +Trading.Network.MessageType.Cancel = "TradeCancel" +Trading.Network.MessageType.Category = "TradeCategory" +Trading.Network.MessageType.Weapon = "TradeWeapon" +Trading.Network.MessageType.WeaponMods = "TradeWeaponMods" +Trading.Network.MessageType.SingleWeaponMod = "TradeSingleWeaponMod" +Trading.Network.MessageType.Mask = "TradeMask" +Trading.Network.MessageType.MaskColour = "TradeColour" +Trading.Network.MessageType.MaskPattern = "TradePattern" +Trading.Network.MessageType.MaskMaterial = "TradeMaterial" + +Trading.Network.RequestTradability = "RequestTradability" +Trading.Network.TradabilityHandshake = "TradabilityHandshake" + +Trading.Network.TradeAccept = "Accept" +Trading.Network.TradeDecline = "Decline" +Trading.Network.AutoDecline_PrimaryWeaponSlots = "DeclinePrimarySlots" +Trading.Network.AutoDecline_SecondaryWeaponSlots = "DeclineSecondarySlots" +Trading.Network.AutoDecline_MaskSlots = "DeclineMaskSlots" +Trading.Network.AutoDecline_AlreadyTrading = "AlreadyTrading" + +-- Options +if not GoonBase.Options.Trading then + GoonBase.Options.Trading = {} + GoonBase.Options.Trading.Enabled = true + GoonBase.Options.Trading.FixedPreferredCharacterMask = false +end + +-- DLC Names +Trading.DLCNames = { + infamous = "Infamy", + twitch_pack = "Twitch Pack", + dlc1 = "Armoured Transport", + armored_transport = "Armoured Transport", + gage_pack = "Gage Weapon Pack #01", + gage_pack_lmg = "Gage Weapon Pack #02", + gage_pack_jobs = "Gage Mod Courier", + gage_pack_snp = "Gage Sniper Pack", + gage_pack_assault = "Gage Assault Pack", + big_bank = "The Big Bank Heist", + gage_pack_shotgun = "Gage Shotgun Pack", + season_pass = "Season Pass", + animal = "Animal" +} + +-- Options Menu +Hooks:Add("MenuManagerSetupCustomMenus", "MenuManagerSetupCustomMenus_Trading", function(menu_manager, menu_nodes) + MenuHelper:NewMenu( Trading.MenuId ) +end) + +Hooks:Add("MenuManagerSetupGoonBaseMenu", "MenuManagerSetupGoonBaseMenu_Trading", function( menu_manager ) + + MenuHelper:AddButton({ + id = "trading_submenu_options_button", + title = "Trading_OptionsMenuTitle", + desc = "Trading_OptionsMenuMessage", + next_node = Trading.MenuId, + menu_id = "goonbase_options_menu", + }) + + MenuCallbackHandler.toggle_trading_enabled = function(this, item) + GoonBase.Options.Trading.Enabled = item:value() == "on" and true or false + GoonBase.Options:Save() + end + + MenuHelper:AddToggle({ + id = "toggle_trading_enabled", + title = "Trading_OptionsTitle", + desc = "Trading_OptionsMessage", + callback = "toggle_trading_enabled", + value = GoonBase.Options.Trading.Enabled, + menu_id = Trading.MenuId, + }) + +end) + +Hooks:Add("MenuManagerBuildCustomMenus", "MenuManagerBuildCustomMenus_Trading", function(menu_manager, mainmenu_nodes) + mainmenu_nodes[Trading.MenuId] = MenuHelper:BuildMenu( Trading.MenuId ) +end) + +-- Blackmarket Menu +Hooks:Add("BlackMarketGUIPostSetup", "BlackMarketGUIPostSetup_InjectTradeButton", function(gui, is_start_page, component_data) + + Hooks:RegisterHook("TradingAttemptTradeWeapon") + gui.trade_weapon_callback = function(self, data) + Hooks:Call("TradingAttemptTradeWeapon", data) + end + + Hooks:RegisterHook("TradingAttemptTradeWeaponMod") + gui.trade_weaponmod_callback = function(self, data) + Hooks:Call("TradingAttemptTradeWeaponMod", data) + end + + Hooks:RegisterHook("TradingAttemptTradeMask") + gui.trade_mask_callback = function(self, data) + Hooks:Call("TradingAttemptTradeMask", data) + end + + Hooks:RegisterHook("TradingAttemptTradeMaskPart") + gui.trade_mask_part_callback = function(self, data) + Hooks:Call("TradingAttemptTradeMaskPart", data) + end + + local w_trade = { + prio = 5, + btn = "BTN_STICK_L", + pc_btn = Idstring("menu_toggle_ready"), + name = "Trading_InventorySendToPlayer", + callback = callback(gui, gui, "trade_weapon_callback") + } + + local m_trade = { + prio = 5, + btn = "BTN_STICK_L", + pc_btn = Idstring("menu_toggle_ready"), + name = "Trading_InventorySendToPlayer", + callback = callback(gui, gui, "trade_mask_callback") + } + + local wm_trade = { + prio = 5, + btn = "BTN_STICK_L", + pc_btn = Idstring("menu_toggle_ready"), + name = "Trading_InventorySendToPlayer", + callback = callback(gui, gui, "trade_weaponmod_callback") + } + + local mp_trade = { + prio = 5, + btn = "BTN_STICK_L", + pc_btn = Idstring("menu_toggle_ready"), + name = "Trading_InventorySendToPlayer", + callback = callback(gui, gui, "trade_mask_part_callback") + } + + local btn_x = 10 + gui._btns["w_trade"] = BlackMarketGuiButtonItem:new(gui._buttons, w_trade, btn_x) + gui._btns["m_trade"] = BlackMarketGuiButtonItem:new(gui._buttons, m_trade, btn_x) + gui._btns["wm_trade"] = BlackMarketGuiButtonItem:new(gui._buttons, wm_trade, btn_x) + gui._btns["mp_trade"] = BlackMarketGuiButtonItem:new(gui._buttons, mp_trade, btn_x) + +end) + +Hooks:Add("MenuUpdate", "MenuUpdate_" .. Mod:ID(), function(t, dt) + + -- This is a mess, but it gets custom keybinds for menu items working + if not managers.menu:is_pc_controller() and managers.menu:get_controller():get_input_pressed("run") then + + local psuccess, perror = pcall(function() + + if not managers.menu_component then return end + + local blackmarket_gui = managers.menu_component._blackmarket_gui + if not blackmarket_gui then return end + if not blackmarket_gui._selected_slot then return end + if not blackmarket_gui._selected_slot._data then return end + + local data = blackmarket_gui._selected_slot._data + if data == nil then return end + local category = data.category + if category == Trading.Categories.PrimaryWeapon or category == Trading.Categories.SecondaryWeapon then + blackmarket_gui:trade_weapon_callback( data ) + end + if category == Trading.Categories.WeaponMod then + blackmarket_gui:trade_weaponmod_callback( data ) + end + if category == Trading.Categories.Mask then + blackmarket_gui:trade_mask_callback( data ) + end + if category == Trading.Categories.MaskColour or category == Trading.Categories.MaskPattern or category == Trading.Categories.MaskMaterial then + blackmarket_gui:trade_mask_part_callback( data ) + end + + end) + if not psuccess then + Print("[Error] " .. perror) + end + + end + +end) + +Hooks:Add("BlackMarketGUIOnPopulateWeaponActionList", "BlackMarketGUIOnPopulateWeaponActionList_" .. Mod:ID(), function(gui, data) + + if GoonBase.Options.Trading.Enabled and not data.last_weapon and not data.equipped and GoonBase.Network:IsMultiplayer() then + table.insert(data, "w_trade") + end + +end) + +Hooks:Add("BlackMarketGUIOnPopulateMasksActionList", "BlackMarketGUIOnPopulateMasksActionList_" .. Mod:ID(), function(gui, data) + + Trading:_FixPreferredCharacterMask() + + if GoonBase.Options.Trading.Enabled and not data.equipped and GoonBase.Network:IsMultiplayer() then + if not Trading.BlacklistedMasks[ data.name ] then + table.insert(data, "m_trade") + end + end + +end) + +Hooks:Add("BlackMarketGUIOnPopulateModsActionList", "BlackMarketGUIOnPopulateModsActionList_" .. Mod:ID(), function(gui, data) + + if GoonBase.Options.Trading.Enabled and GoonBase.Network:IsMultiplayer() then + if type(data.unlocked) ~= "number" or data.unlocked > 0 then + table.insert(data, "wm_trade") + end + end + +end) + +Hooks:Add("BlackMarketGUIOnPopulateMaskModsActionList", "BlackMarketGUIOnPopulateMaskModsActionList_" .. Mod:ID(), function(gui, data) + + if GoonBase.Options.Trading.Enabled and not data.equipped and data.amount ~= nil and data.amount > 0 and GoonBase.Network:IsMultiplayer() then + table.insert(data, "mp_trade") + end + +end) + +-- Request client tradability when we open the inventory +Hooks:Add("BlackMarketGUIOnPopulateWeapons", "BlackMarketGUIOnPopulateWeapons_Trading", function(gui, category, data) + + if not GoonBase.Options.Trading.Enabled then + return + end + + Trading.BlackMarketGUI = gui + + -- Request tradability from peers + if GoonBase.Network:IsMultiplayer() then + Trading.TradablePeers = {} + GoonBase.Network:SendToPeers(Trading.Network.MessageType.System, Trading.Network.RequestTradability) + end + +end) + +Hooks:Add("BlackMarketGUIOnPopulateMasks", "BlackMarketGUIOnPopulateMasks_Trading", function(gui, category, data) + + if not GoonBase.Options.Trading.Enabled then + return + end + + Trading.BlackMarketGUI = gui + +end) + +Hooks:Add("BlackMarketGUIOnPopulateMods", "BlackMarketGUIOnPopulateMods_Trading", function(gui, category, data) + + if not GoonBase.Options.Trading.Enabled then + return + end + + Trading.BlackMarketGUI = gui + +end) + +Hooks:Add("BlackMarketGUIOnPopulateMaskMods", "BlackMarketGUIOnPopulateMaskMods_Trading", function(gui, category, data) + + if not GoonBase.Options.Trading.Enabled then + return + end + + Trading.BlackMarketGUI = gui + +end) + +-- Check if we have any tradable peers +function Trading:HasTradablePeers() + + local tradable_peers = 0 + for k, v in pairs( managers.network:session():peers() ) do + if Trading.TradablePeers[k] == true then + tradable_peers = tradable_peers + 1 + end + end + + if tradable_peers < 1 then + return false + end + + return true + +end + +-- Process trade messages +Hooks:Add("NetworkReceivedData", "NetworkReceivedData_Trading", function(sender, messageType, data) + + -- Don't process if disabled + if not GoonBase.Options.Trading.Enabled then + return + end + + -- Trade System Messages + if messageType == Trading.Network.MessageType.System then + + if data == Trading.Network.RequestTradability then + Trading:RequestTradabilityHandshake(sender) + return + end + if data == Trading.Network.TradabilityHandshake then + Trading:CompleteTradabilityHandshake(sender) + return + end + + end + + -- Trade Response + if messageType == Trading.Network.MessageType.RequestResponse then + Trading:TradeResponseReceived(sender, data) + return + end + + -- Trade Already-In-Progress Reply + if Trading.TradeData.CurrentlyTradingWith ~= nil and Trading.TradeData.CurrentlyTradingWith ~= sender then + GoonBase.Network:SendToPeer(sender, Trading.Network.MessageType.RequestResponse, Trading.Network.AutoDecline_AlreadyTrading) + return + end + + -- Make sure player only ever gets trade data from one player + Trading.TradeData.CurrentlyTradingWith = sender + + -- Trade Requests + if messageType == Trading.Network.MessageType.Request then + Trading:TradeRequested(sender) + return + end + + -- Trade Cancel + if messageType == Trading.Network.MessageType.Cancel then + Trading:HandleTradeCancelled(sender) + return + end + + -- Trade Data + if messageType == Trading.Network.MessageType.Category then + Trading.TradeData.Category = data + return + end + + if messageType == Trading.Network.MessageType.Weapon then + Trading.TradeData.Weapon = data + return + end + + if messageType == Trading.Network.MessageType.WeaponMods then + if Trading.TradeData.WeaponMods == nil then + Trading.TradeData.WeaponMods = {} + end + table.insert( Trading.TradeData.WeaponMods, data ) + return + end + + if messageType == Trading.Network.MessageType.SingleWeaponMod then + Trading.TradeData.WeaponMod = data + return + end + + -- Mask Data + if messageType == Trading.Network.MessageType.Mask then + Trading.TradeData.Mask = data + end + + -- Mask Mod Data + if messageType == Trading.Network.MessageType.MaskPattern then + if Trading.TradeData.MaskMods == nil then + Trading.TradeData.MaskMods = {} + end + Trading.TradeData.MaskMods.Pattern = data + return + end + + if messageType == Trading.Network.MessageType.MaskMaterial then + if Trading.TradeData.MaskMods == nil then + Trading.TradeData.MaskMods = {} + end + Trading.TradeData.MaskMods.Material = data + return + end + + if messageType == Trading.Network.MessageType.MaskColour then + if Trading.TradeData.MaskMods == nil then + Trading.TradeData.MaskMods = {} + end + Trading.TradeData.MaskMods.Colour = data + return + end + +end) + +-- Handle tradability handshake +function Trading:RequestTradabilityHandshake(sender) + if GoonBase.Options.Trading.Enabled then + GoonBase.Network:SendToPeer(sender, Trading.Network.MessageType.System, Trading.Network.TradabilityHandshake) + end +end +function Trading:CompleteTradabilityHandshake(sender) + Trading.TradablePeers[sender] = true +end + +-- Handle trade request +function Trading:TradeRequested(sender, data) + + Trading.TradeData.Trader = sender + local category = Trading.TradeData.Category + + -- Check if player has enough primary weapon slots to trade + if category == Trading.Categories.PrimaryWeapon then + + local slot = managers.blackmarket:_get_free_weapon_slot( Trading.Categories.PrimaryWeapon ) + local weaponName = managers.weapon_factory:get_weapon_name_by_weapon_id( Trading.TradeData.Weapon ) + if slot ~= nil then + + Trading:ShowTradeResponseWindow(sender, { name = weaponName, category = "Primary Weapon" }) + else + Trading:ShowFullInventoryWindow(sender, { item = weaponName, slot = "primary weapon" }) + GoonBase.Network:SendToPeer(sender, Trading.Network.MessageType.RequestResponse, Trading.Network.AutoDecline_PrimaryWeaponSlots) + end + + end + + -- Check if player has enough secondary weapon slots to trade + if category == Trading.Categories.SecondaryWeapon then + + local slot = managers.blackmarket:_get_free_weapon_slot( Trading.Categories.SecondaryWeapon ) + local weaponName = managers.weapon_factory:get_weapon_name_by_weapon_id( Trading.TradeData.Weapon ) + if slot ~= nil then + Trading:ShowTradeResponseWindow(sender, { name = weaponName, category = "Secondary Weapon" }) + else + Trading:ShowFullInventoryWindow(sender, { item = weaponName, slot = "secondary weapon" }) + GoonBase.Network:SendToPeer(sender, Trading.Network.MessageType.RequestResponse, Trading.Network.AutoDecline_SecondaryWeaponSlots) + end + + end + + -- Single Weapon Mod Trade + if category == Trading.Categories.WeaponMod then + local modName = managers.weapon_factory:get_part_name_by_part_id( Trading.TradeData.WeaponMod ) + Trading:ShowTradeResponseWindow(sender, { name = modName, category = "Weapon Mod" }) + end + + -- Mask Trading + local success, err = pcall(function() + + if category == Trading.Categories.Mask then + + local mask_name = managers.blackmarket:get_mask_name( Trading.TradeData.Mask ) + + -- Check if player is trying to send premodded mask + if Trading.TradeData.MaskMods ~= nil then + + -- Check if player has free mask slots + local slot = managers.blackmarket:get_free_mask_slot() + if slot ~= nil then + Trading:ShowTradeResponseWindow(sender, { name = mask_name or "error: mask_name", category = "Mask" }) + else + Trading:ShowFullInventoryWindow(sender, { item = mask_name or "error: mask_name", slot = "mask" }) + GoonBase.Network:SendToPeer(sender, Trading.Network.MessageType.RequestResponse, Trading.Network.AutoDecline_MaskSlots) + end + + else + Trading:ShowTradeResponseWindow(sender, { name = mask_name or "error: mask_name", category = "Mask" }) + end + + end + + -- Mask Material + if category == Trading.Categories.MaskMaterial then + local mod_name = managers.blackmarket:get_mask_mod_name( Trading.TradeData.MaskMods.Material, Trading.Categories.MaskMaterial ) + Trading:ShowTradeResponseWindow(sender, { name = mod_name or "error: mod_name", category = "Mask Material" }) + end + + -- Mask Pattern + if category == Trading.Categories.MaskPattern then + local mod_name = managers.blackmarket:get_mask_mod_name( Trading.TradeData.MaskMods.Pattern, Trading.Categories.MaskPattern ) + Trading:ShowTradeResponseWindow(sender, { name = mod_name or "error: mod_name", category = "Mask Pattern" }) + end + + -- Mask Color + if category == Trading.Categories.MaskColour then + local mod_name = managers.blackmarket:get_mask_mod_name( Trading.TradeData.MaskMods.Colour, Trading.Categories.MaskColour ) + Trading:ShowTradeResponseWindow(sender, { name = mod_name or "error: mod_name", category = "Mask Colours" }) + end + + end) + if not success then Print(err) end + +end + +function Trading:ShowTradeResponseWindow(sender, data) + + local senderName = GoonBase.Network:GetNameFromPeerID(sender) + + local title = managers.localization:text("Trading_TradeWindowTitle") + local message = managers.localization:text("Trading_TradeRequestMessage") + message = message:gsub("{1}", senderName) + message = message:gsub("{2}", data.name) + message = message:gsub("{3}", data.category) + local menuOptions = {} + menuOptions[1] = { + text = managers.localization:text("Trading_TradeRequestAccept"), + callback = Trading.TradeRequestAccept, + is_cancel_button = true + } + menuOptions[2] = { + text = managers.localization:text("Trading_TradeRequestDecline"), + callback = Trading.TradeRequestDecline, + is_cancel_button = true + } + Trading.ActiveTradeWindow = QuickMenu:new(title, message, menuOptions, true) + +end + +function Trading:ShowFullInventoryWindow(sender, data) + + local senderName = GoonBase.Network:GetNameFromPeerID(sender) + + local title = managers.localization:text("Trading_TradeWindowTitle") + local message = managers.localization:text("Trading_InventoryFull") + message = message:gsub("{1}", senderName) + message = message:gsub("{2}", data.item) + message = message:gsub("{3}", data.slot) + local menuOptions = {} + menuOptions[1] = { + text = managers.localization:text("Trading_TradeWindowOk"), + is_cancel_button = true + } + local tradeMenu = QuickMenu:new(title, message, menuOptions, true) + +end + +function Trading.TradeRequestAccept(data) + + GoonBase.Network:SendToPeer(Trading.TradeData.Trader, Trading.Network.MessageType.RequestResponse, Trading.Network.TradeAccept) + + local category = Trading.TradeData.Category + + if category == Trading.Categories.PrimaryWeapon then + Trading:AddWeaponToInventory( Trading.TradeData.Weapon, Trading.TradeData.WeaponMods ) + managers.menu:back(true) + end + + if category == Trading.Categories.SecondaryWeapon then + Trading:AddWeaponToInventory( Trading.TradeData.Weapon, Trading.TradeData.WeaponMods ) + managers.menu:back(true) + end + + if category == Trading.Categories.WeaponMod then + Trading:AddTradedModToInventory() + managers.menu:back(true) + managers.menu:back(true) + end + + if category == Trading.Categories.Mask then + Trading:AddTradedMaskToInventory() + managers.menu:back(true) + end + + if category == Trading.Categories.MaskMaterial or category == Trading.Categories.MaskPattern or category == Trading.Categories.MaskColour then + Trading:AddTradedMaskModToInventory() + end + + Trading.TradeData = {} + +end + +function Trading.TradeRequestDecline(data) + GoonBase.Network:SendToPeer(Trading.TradeData.Trader, Trading.Network.MessageType.RequestResponse, Trading.Network.TradeDecline) + Trading.TradeData = {} +end + +-- Handle Trade Response +function Trading:TradeResponseReceived(sender, data) + + if data == Trading.Network.TradeAccept then + Trading:TradeResponseAccept(sender) + return + end + + if data == Trading.Network.AutoDecline_PrimaryWeaponSlots then + Trading:TradeResponseFullInventory("primary weapon") + return + end + + if data == Trading.Network.AutoDecline_SecondaryWeaponSlots then + Trading:TradeResponseFullInventory("secondary weapon") + return + end + + if data == Trading.Network.AutoDecline_MaskSlots then + Trading:TradeResponseFullInventory("mask") + return + end + + if data == Trading.Network.AutoDecline_AlreadyTrading then + Trading:TradeResponseAlreadyTrading() + return + end + + Trading:TradeResponseDecline(sender) + +end + +function Trading:TradeResponseAccept(sender) + + local success, err = pcall(function() + + local category = Trading.TradeData.Category + + if category == Trading.Categories.PrimaryWeapon or category == Trading.Categories.SecondaryWeapon then + Trading:ShowTradeResponseAcceptWindow( sender, Trading.TradeData.RawData.slot.name_localized ) + Trading:RemoveWeaponFromInventory() + managers.menu:back(true) + end + + if category == Trading.Categories.WeaponMod then + Trading:ShowTradeResponseAcceptWindow( sender, Trading.TradeData.RawData.slot.name_localized ) + Trading:RemoveModFromInventory() + managers.menu:back(true) + managers.menu:back(true) + end + + if category == Trading.Categories.Mask then + Trading:ShowTradeResponseAcceptWindow( sender, managers.blackmarket:get_mask_name( Trading.TradeData.RawData.slot.mask_id ) ) + managers.blackmarket:remove_mask_from_inventory( Trading.TradeData.RawData.slot ) + managers.menu:back(true) + end + + if category == Trading.Categories.MaskMaterial or category == Trading.Categories.MaskPattern or category == Trading.Categories.MaskColour then + Trading:ShowTradeResponseAcceptWindow( sender, managers.blackmarket:get_mask_mod_name( Trading.TradeData.RawData.slot.name, Trading.TradeData.RawData.slot.category ) ) + -- TODO + managers.blackmarket:remove_mask_mod_from_inventory( Trading.TradeData.RawData.slot.name, Trading.TradeData.RawData.slot.category ) + end + + end) + + if not success then Print(err) end + +end + +function Trading:ShowTradeResponseAcceptWindow(sender, item_name) + + Trading:CloseActiveWindow() + + local title = managers.localization:text("Trading_TradeWindowTitle") + local message = managers.localization:text("Trading_TradeAccepted") + message = message:gsub("{1}", item_name) + message = message:gsub("{2}", GoonBase.Network:GetNameFromPeerID(Trading.TradeData.Recipient)) + local menuOptions = {} + menuOptions[1] = { text = managers.localization:text("Trading_TradeWindowOk"), is_cancel_button = true } + local tradeMenu = QuickMenu:new(title, message, menuOptions, true) + +end + +function Trading:TradeResponseDecline(sender) + + local success, err = pcall(function() + + Trading:CloseActiveWindow() + + if Trading.TradeData.Recipient == nil then + return + end + + local category = Trading.TradeData.Category + local item_name = "trade" + if category == Trading.Categories.PrimaryWeapon or category == Trading.Categories.SecondaryWeapon or category == Trading.Categories.WeaponMod then + item_name = Trading.TradeData.RawData.slot.name_localized or item_name + end + if category == Trading.Categories.Mask then + item_name = managers.blackmarket:get_mask_name( Trading.TradeData.RawData.slot.mask_id ) + end + + local title = managers.localization:text("Trading_TradeWindowTitle") + local message = managers.localization:text("Trading_TradeDeclined") + message = message:gsub("{1}", GoonBase.Network:GetNameFromPeerID(Trading.TradeData.Recipient)) + message = message:gsub("{2}", item_name) + local menuOptions = {} + menuOptions[1] = { text = managers.localization:text("Trading_TradeWindowOk"), is_cancel_button = true } + local tradeMenu = QuickMenu:new(title, message, menuOptions, true) + + Trading.TradeData = {} + + end) + if not success then Print(err) end + +end + +function Trading:TradeResponseAlreadyTrading() + + local success, err = pcall(function() + + Trading:CloseActiveWindow() + + if Trading.TradeData.Recipient == nil then + return + end + + local title = managers.localization:text("Trading_AlreadyTradingTitle") + local message = managers.localization:text("Trading_AlreadyTradingMessage") + message = message:gsub("{1}", GoonBase.Network:GetNameFromPeerID(Trading.TradeData.Recipient)) + local menuOptions = {} + menuOptions[1] = { text = managers.localization:text("Trading_AlreadyTradingAccept"), is_cancel_button = true } + local tradeMenu = QuickMenu:new(title, message, menuOptions, true) + + Trading.TradeData = {} + + end) + if not success then Print(err) end + +end + +function Trading:TradeResponseFullInventory(reason) + + Trading:CloseActiveWindow() + + local title = managers.localization:text("Trading_TradeWindowTitle") + local message = managers.localization:text("Trading_InventoryFullReason") + message = message:gsub("{1}", managers.weapon_factory:get_weapon_name_by_weapon_id(Trading.TradeData.Weapon)) + message = message:gsub("{2}", GoonBase.Network:GetNameFromPeerID(Trading.TradeData.Recipient)) + message = message:gsub("{3}", reason) + local menuOptions = {} + menuOptions[1] = { text = managers.localization:text("Trading_TradeWindowOk"), is_cancel_button = true } + local tradeMenu = QuickMenu:new(title, message, menuOptions, true) + +end + +-- Show Trade Menu when player clicks the trade button +Hooks:Add("TradingAttemptTradeWeapon", "TradingAttemptTradeWeapon_ShowMenu", function(weapon) + Trading:ShowSendToPlayerMenu(weapon.name_localized, weapon) +end) + +Hooks:Add("TradingAttemptTradeWeaponMod", "TradingAttemptTradeWeaponMod_ShowMenu", function(mod) + -- Mods use 'primaries' and 'secondaries' which we use for weapons + -- So we use a custom category to make the trading work with them + mod.category = Trading.Categories.WeaponMod + Trading:ShowSendToPlayerMenu(mod.name_localized, mod) +end) + +Hooks:Add("TradingAttemptTradeMask", "TradingAttemptTradeMask_ShowMenu", function(mask) + + local data = managers.blackmarket:get_mask_slot_data(mask) + if data == nil then + return + end + data.category = "masks" + data.slot = mask.slot + + -- Verify mask parts before sending the mask + -- Some mods get returned to the inventory when selling the mask, stop masks with these on from being traded + -- so that we don't duplicate mods. TODO: Ask Overkill about this before release! + if Trading:VerifyMaskTradability(data) then + Trading:ShowSendToPlayerMenu(mask.name_localized, data) + end + +end) + +Hooks:Add("TradingAttemptTradeMaskPart", "TradingAttemptTradeMaskPart_ShowMenu", function(mod) + + -- Verify mask mod before sending the mask + if Trading:VerifyMaskModTradability(mod) then + Trading:ShowSendToPlayerMenu(mod.name_localized, mod) + end + +end) + +function Trading:ShowSendToPlayerMenu(item_name, item_data) + + if GoonBase.Network:IsMultiplayer() then + + -- Check for tradable peers + if not Trading:HasTradablePeers() then + Trading:NoTradablePeersWindow() + return + end + + -- Show tradable peers + local title = managers.localization:text("Trading_TradeWindowTitle") + local message = managers.localization:text("Trading_TradeWindowMessage"):gsub("{1}", item_name) + local menuOptions = {} + + for k, v in pairs( managers.network:session():peers() ) do + + if Trading.TradablePeers[k] == true then + + local plyData = { + text = v._name, + callback = Trading.SendToPlayerCallback, + data = { + peer = k, + slot = item_data + } + } + table.insert( menuOptions, plyData ) + + end + + end + table.insert( menuOptions, { text = managers.localization:text("Trading_TradeWindowCancel"), is_cancel_button = true } ) + + local tradeMenu = QuickMenu:new(title, message, menuOptions, true) + + else + Trading:ShowOnlineOnlyWindow() + end + +end + +function Trading.SendToPlayerCallback(data) + + local success, err = pcall(function() + + local category = data.slot.category + Trading.TradeData = { + Recipient = data.peer, + Category = category, + RawData = data + } + + -- Send category to recepient + GoonBase.Network:SendToPeer(data.peer, Trading.Network.MessageType.Category, category) + + -- Weapon trading + if category == Trading.Categories.PrimaryWeapon or category == Trading.Categories.SecondaryWeapon then + + -- Ask if we should send just the weapon or with the mods + local title = managers.localization:text("Trading_TradeWindowTitle") + local message = managers.localization:text("Trading_WeaponSendWithMods") + local menuOptions = {} + menuOptions[1] = { + text = managers.localization:text("Trading_WeaponSendMods"), + callback = Trading.CallbackSendWeaponToPlayer, + data = { + trade_data = data, + send_mods = true + } + } + menuOptions[2] = { + text = managers.localization:text("Trading_WeaponDontSendMods"), + callback = Trading.CallbackSendWeaponToPlayer, + data = { + trade_data = data, + send_mods = false + } + } + local tradeMenu = QuickMenu:new(title, message, menuOptions, true) + + end + + -- Weapon Mod Trading + if category == Trading.Categories.WeaponMod then + Trading:CallbackSendModToPlayer(data) + end + + -- Mask Trading + if category == Trading.Categories.Mask then + Trading:CallbackSendMaskToPlayer(data) + end + + -- Mask Material/Pattern/Colour Trading + if category == Trading.Categories.MaskMaterial or category == Trading.Categories.MaskPattern or category == Trading.Categories.MaskColour then + Trading:CallbackSendMaskModToPlayer(data) + end + + end) + if not success then Print(err) end + +end + +function Trading.CallbackSendWeaponToPlayer(data) + + local trade_data = data.trade_data + Trading.TradeData.SendWeaponMods = data.send_mods + + local weaponMods = BlackMarketManager:get_mods_on_weapon(trade_data.slot.category, trade_data.slot.slot) + GoonBase.Network:SendToPeer(trade_data.peer, Trading.Network.MessageType.Weapon, trade_data.slot.name) + + if data.send_mods then + for i, modName in pairs(weaponMods) do + GoonBase.Network:SendToPeer(trade_data.peer, Trading.Network.MessageType.WeaponMods, modName) + end + end + + GoonBase.Network:SendToPeer(trade_data.peer, Trading.Network.MessageType.Request, "") + Trading.TradeData.Weapon = trade_data.slot.name + Trading.TradeData.WeaponMods = weaponMods + Trading.TradeData.Slot = trade_data.slot + + -- Show waiting window + Trading:ShowWaitingResponseWindow() + +end + +function Trading:CallbackSendModToPlayer(data) + + -- Send trade data to player + GoonBase.Network:SendToPeer(data.peer, Trading.Network.MessageType.SingleWeaponMod, data.slot.name) + + -- Send request to player + GoonBase.Network:SendToPeer(data.peer, Trading.Network.MessageType.Request, "") + + -- Show waiting window + Trading:ShowWaitingResponseWindow() + +end + +function Trading:CallbackSendMaskToPlayer(data) + + -- Table stuff + local peer = data.peer + data = data.slot + + -- Send modded mask data to player + if data.modded then + GoonBase.Network:SendToPeer(peer, Trading.Network.MessageType.MaskMaterial, data.blueprint.material.id) + GoonBase.Network:SendToPeer(peer, Trading.Network.MessageType.MaskPattern, data.blueprint.pattern.id) + GoonBase.Network:SendToPeer(peer, Trading.Network.MessageType.MaskColour, data.blueprint.color.id) + end + + -- Send mask data to player + GoonBase.Network:SendToPeer(peer, Trading.Network.MessageType.Mask, data.mask_id) + + -- Send request to player + GoonBase.Network:SendToPeer(peer, Trading.Network.MessageType.Request, "") + + -- Show waiting window + Trading:ShowWaitingResponseWindow() + +end + +function Trading:CallbackSendMaskModToPlayer(data) + + -- Table stuff + local peer = data.peer + data = data.slot + + -- Send data to player + if data.category == Trading.Categories.MaskMaterial then + GoonBase.Network:SendToPeer(peer, Trading.Network.MessageType.MaskMaterial, data.name) + end + if data.category == Trading.Categories.MaskPattern then + GoonBase.Network:SendToPeer(peer, Trading.Network.MessageType.MaskPattern, data.name) + end + if data.category == Trading.Categories.MaskColour then + GoonBase.Network:SendToPeer(peer, Trading.Network.MessageType.MaskColour, data.name) + end + + -- Send request + GoonBase.Network:SendToPeer(peer, Trading.Network.MessageType.Request, "") + + -- Show waiting window + Trading:ShowWaitingResponseWindow() + +end + +-- Trading Offline Screen +function Trading:ShowOnlineOnlyWindow() + + local title = managers.localization:text("Trading_OnlineOnlyTitle") + local message = managers.localization:text("Trading_OnlineOnlyMessage") + local menuOptions = {} + menuOptions[1] = { text = managers.localization:text("Trading_OnlineOnlyCancel"), is_cancel_button = true } + local tradeMenu = QuickMenu:new(title, message, menuOptions, true) + +end + +-- No Tradable Peers Screen +function Trading:NoTradablePeersWindow() + + local title = managers.localization:text("Trading_NoPeersTitle") + local message = managers.localization:text("Trading_NoPeersMessage") + local menuOptions = {} + menuOptions[1] = { text = managers.localization:text("Trading_NoPeersCancel"), is_cancel_button = true } + local tradeMenu = QuickMenu:new(title, message, menuOptions, true) + +end + +-- Trade Wait Screen +function Trading:ShowWaitingResponseWindow() + + local title = managers.localization:text("Trading_WaitingResponseTitle") + local message = managers.localization:text("Trading_WaitingResponseMessage") + message = message:gsub("{1}", GoonBase.Network:GetNameFromPeerID(Trading.TradeData.Recipient)) + local menuOptions = {} + menuOptions[1] = { + text = managers.localization:text("Trading_WaitingResponseCancel"), + callback = Trading.WaitingResponseCancel, + } + Trading.ActiveTradeWindow = QuickMenu:new(title, message, menuOptions) + Trading.ActiveTradeWindow.dialog_data.indicator = true + Trading.ActiveTradeWindow:Show() + +end + +-- Trade Cancelling +function Trading.WaitingResponseCancel() + + -- Close trade waiting screen + Trading:CloseActiveWindow() + + -- Tell recipient to clear trade data + GoonBase.Network:SendToPeer(Trading.TradeData.Recipient, Trading.Network.MessageType.Cancel, "") + + -- Clear local trade data + Trading.TradeData = {} + +end + +function Trading:HandleTradeCancelled(sender) + + -- Remove trade response window + Trading:CloseActiveWindow() + + -- Show trade cancelled window + local title = managers.localization:text("Trading_OtherCancelledTitle") + local message = managers.localization:text("Trading_OtherCancelledMessage") + message = message:gsub("{1}", GoonBase.Network:GetNameFromPeerID(Trading.TradeData.Trader)) + local menuOptions = {} + menuOptions[1] = { + text = managers.localization:text("Trading_OtherCancelledAccept"), + is_cancel_button = true + } + Trading.ActiveTradeWindow = QuickMenu:new(title, message, menuOptions) + Trading.ActiveTradeWindow.dialog_data.indicator = true + Trading.ActiveTradeWindow:Show() + + -- Clear local trade data + Trading.TradeData = {} + +end + +-- Windows +function Trading:CloseActiveWindow() + if Trading.ActiveTradeWindow ~= nil then + managers.system_menu:close(Trading.ActiveTradeWindow.dialog_data.id) + Trading.ActiveTradeWindow.visible = false + Trading.ActiveTradeWindow = nil + end +end + +-- Weapon Inventory +function Trading:_GetWeaponTweakData(weapon) + return tweak_data.weapon[weapon] +end + +function Trading:AddWeaponToInventory(weapon, mods) + + local category = Trading.TradeData.Category or "primaries" + local freeSlot = managers.blackmarket:_get_free_weapon_slot(category) + + if freeSlot ~= nil then + + managers.blackmarket:on_buy_weapon_platform(category, weapon, freeSlot, true) + + if mods ~= nil then + + -- Keep track of mods we can't equip + local banned_mods = {} + + -- Attach mods + for k, v in pairs(mods) do + local factory = tweak_data.weapon.factory.parts[v] + if factory then + + -- Attach weapons for DLC we own, add it to the inventory for mods we dont + if factory.dlc ~= nil and not managers.dlc:has_dlc(factory.dlc) then + banned_mods[v] = factory.dlc + managers.blackmarket:add_to_inventory(factory.dlc, "weapon_mods", v, true) + else + managers.blackmarket:buy_and_modify_weapon(category, freeSlot, "normal", v, true, true) + end + + end + end + + -- Warn us about mods we can't use + for k, v in pairs(banned_mods) do + Trading:ShowModDLCNotOwned(tweak_data.weapon.factory.parts[k].name_id, tweak_data.weapon.factory.parts[k].dlc) + end + + end + + end + +end + +function Trading:ShowModDLCNotOwned(mod, dlc) + + mod = managers.localization:text(mod) + dlc = Trading.DLCNames[dlc] or ("error: " .. dlc) + + local title = managers.localization:text("Trading_TradeWindowTitle") + local message = managers.localization:text("Trading_WeaponModRemovedDLC") + message = message:gsub("{1}", mod) + message = message:gsub("{2}", dlc) + local menuOptions = {} + menuOptions[1] = { text = managers.localization:text("Trading_TradeWindowOk"), is_cancel_button = true } + local tradeMenu = QuickMenu:new(title, message, menuOptions, true) + +end + +function Trading:RemoveWeaponFromInventory() + + local category = Trading.TradeData.Slot.category + local slot = Trading.TradeData.Slot.slot + local remove_mods = not Trading.TradeData.SendWeaponMods + + -- Remove weapon and mods + local success, err = pcall(function() + managers.blackmarket:on_traded_weapon(category, slot, remove_mods) + end) + if not success then Print(err) end + + -- Reload gui + if Trading.BlackMarketGUI ~= nil then + Trading.BlackMarketGUI:reload() + end + +end + +function Trading:RemoveModFromInventory() + + managers.blackmarket:on_traded_mod(Trading.TradeData.RawData.slot.name) + +end + +function Trading:AddTradedModToInventory() + + local success, err = pcall(function() + + local mod = Trading.TradeData.WeaponMod + + -- Show message about mod + local factory = tweak_data.weapon.factory.parts[mod] + if factory then + + local mod_name = managers.localization:text(factory.name_id) + local ply_name = GoonBase.Network:GetNameFromPeerID(Trading.TradeData.Trader) + + -- Attach weapons for DLC we own, add it to the inventory for mods we dont + if factory.dlc ~= nil and not managers.dlc:has_dlc(factory.dlc) then + + local dlc_name = Trading.DLCNames[factory.dlc] or ("error: " .. factory.dlc) + + -- DLC is not owned, show different message + local title = managers.localization:text("Trading_TradeWindowTitle") + local message = managers.localization:text("Trading_WeaponModReceivedNoDLC") + message = message:gsub("{1}", mod_name) + message = message:gsub("{2}", ply_name) + message = message:gsub("{3}", dlc_name) + local menuOptions = {} + menuOptions[1] = { text = managers.localization:text("Trading_TradeWindowOk"), is_cancel_button = true } + local tradeMenu = QuickMenu:new(title, message, menuOptions, true) + + else + + -- Show default message + local title = managers.localization:text("Trading_TradeWindowTitle") + local message = managers.localization:text("Trading_WeaponModReceived") + message = message:gsub("{1}", mod_name) + message = message:gsub("{2}", ply_name) + local menuOptions = {} + menuOptions[1] = { text = managers.localization:text("Trading_TradeWindowOk"), is_cancel_button = true } + local tradeMenu = QuickMenu:new(title, message, menuOptions, true) + + end + + end + + -- Add mod to inventory + managers.blackmarket:on_received_traded_mod( Trading.TradeData.WeaponMod ) + + end) + if not success then Print(err) end + +end + +function Trading:AddTradedMaskToInventory() + + local success, err = pcall(function() + + local mask_id = Trading.TradeData.Mask + + if Trading.TradeData.MaskMods ~= nil then + + -- Adding mods to mask + local material = Trading.TradeData.MaskMods.Material + local pattern = Trading.TradeData.MaskMods.Pattern + local color = Trading.TradeData.MaskMods.Colour + + -- Check if user has all DLC for mask + local dlc_check = managers.blackmarket:has_all_dlc_for_mask_and_parts(mask_id, material, pattern, color) + if type(dlc_check) == "table" then + + -- User doesn't have all DLC, add items to inventory instead + managers.blackmarket:add_traded_mask_to_inventory(mask_id) + managers.blackmarket:add_traded_mask_part_to_inventory(material, Trading.Categories.MaskMaterial) + managers.blackmarket:add_traded_mask_part_to_inventory(pattern, Trading.Categories.MaskPattern) + managers.blackmarket:add_traded_mask_part_to_inventory(color, Trading.Categories.MaskColour) + + -- Localization strings + local mask_name = managers.blackmarket:get_mask_name( Trading.TradeData.Mask ) + local player_name = GoonBase.Network:GetNameFromPeerID( Trading.TradeData.Trader ) + local missing_dlcs = "" + + -- Show DLC warning + for k, v in pairs( dlc_check ) do + if missing_dlcs ~= "" then + missing_dlcs = missing_dlcs .. ", or " + end + missing_dlcs = (missing_dlcs .. "'{1}'"):gsub("{1}", Trading.DLCNames[k] or k) + end + Trading:ShowMaskTradeDLCWarning(mask_name, player_name, missing_dlcs) + + return + end + + -- Add modded mask to inventory + managers.blackmarket:add_traded_modded_mask_to_free_slot(mask_id, material, pattern, color) + + else + + -- Only sending mask, add to inventory + managers.blackmarket:add_traded_mask_to_inventory(mask_id) + + -- Check player owns DLC + local missing_dlc = managers.blackmarket:has_all_dlc_for_mask(mask_id) + if type(missing_dlc) == "string" then + local mask_name = managers.blackmarket:get_mask_name( Trading.TradeData.Mask ) + local player_name = GoonBase.Network:GetNameFromPeerID( Trading.TradeData.Trader ) + local dlc_name = ("'{1}'"):gsub("{1}", Trading.DLCNames[missing_dlc] or missing_dlc) + Trading:ShowMaskTradeDLCWarning(mask_name, player_name, dlc_name) + end + + end + + end) + if not success then Print(err) end + +end + +function Trading:AddTradedMaskModToInventory() + + local success, err = pcall(function() + + -- Get mod ID and check mod is valid + local mod_id = nil + local category = Trading.TradeData.Category + + if category == Trading.Categories.MaskMaterial then + mod_id = Trading.TradeData.MaskMods.Material + end + if category == Trading.Categories.MaskPattern then + mod_id = Trading.TradeData.MaskMods.Pattern + end + if category == Trading.Categories.MaskColour then + mod_id = Trading.TradeData.MaskMods.Colour + end + if mod_id == nil then + return + end + + -- Add mod to inventory + managers.blackmarket:add_traded_mask_part_to_inventory(mod_id, category) + + -- Warn about DLC + local missing_dlc = managers.blackmarket:has_all_dlc_for_mask_mod(mod_id, category) + if type(missing_dlc) == "string" then + local mod_name = managers.blackmarket:get_mask_mod_name( mod_id, category ) + local player_name = GoonBase.Network:GetNameFromPeerID( Trading.TradeData.Trader ) + local dlc_name = ("'{1}'"):gsub("{1}", Trading.DLCNames[missing_dlc] or missing_dlc) + Trading:ShowMaskTradeDLCWarning(mod_name, player_name, dlc_name) + end + + end) + if not success then Print(err) end + +end + +function Trading:ShowMaskTradeDLCWarning(mask_name, player_name, missing_dlcs) + + local title = managers.localization:text("Trading_TradeWindowTitle") + local message = managers.localization:text("Trading_MaskReceivedNoDLC") + message = message:gsub("{1}", mask_name) + message = message:gsub("{2}", player_name) + message = message:gsub("{3}", missing_dlcs) + local menuOptions = {} + menuOptions[1] = { + text = managers.localization:text("Trading_MaskReceivedAccept"), + is_cancel_button = true + } + local tradeMenu = QuickMenu:new(title, message, menuOptions, true) + +end + +function Trading:VerifyMaskTradability(data) + + -- Items which are untradable + local untradable = nil + + -- Check mask itself is fine to trade + local mask_data = managers.blackmarket:get_mask_data( data.mask_id ) + if mask_data == nil then + return + end + if mask_data.dlc ~= nil and mask_data.value == 0 then + if untradable == nil then untradable = {} end + untradable["masks"] = data.mask_id + end + + -- Check mask components are ok + if data.blueprint ~= nil then + + -- Material + local material_data = managers.blackmarket:get_mask_mod_data( data.blueprint.material.id, Trading.Categories.MaskMaterial ) + if material_data ~= nil and material_data.dlc ~= nil and material_data.value == 0 then + if untradable == nil then untradable = {} end + untradable["materials"] = data.blueprint.material.id + end + + -- Pattern + local pattern_data = managers.blackmarket:get_mask_mod_data( data.blueprint.pattern.id, Trading.Categories.MaskPattern ) + if pattern_data ~= nil and pattern_data.dlc ~= nil and pattern_data.value == 0 then + if untradable == nil then untradable = {} end + untradable["textures"] = data.blueprint.pattern.id + end + + -- Colour + local color_data = managers.blackmarket:get_mask_mod_data( data.blueprint.color.id, Trading.Categories.MaskColour ) + if color_data ~= nil and color_data.dlc ~= nil and color_data.value == 0 then + if untradable == nil then untradable = {} end + untradable["colors"] = data.blueprint.color.id + end + + end + + -- Show untradables + if untradable ~= nil then + Trading:ShowMaskTradabilityMessage(managers.blackmarket:get_mask_name(data.mask_id), untradable) + return false + end + + -- Mask is clear, good to trade + return true + +end + +function Trading:VerifyMaskModTradability(mod, category) + + local mod_data = managers.blackmarket:get_mask_mod_data( mod.name, mod.category ) + if mod_data ~= nil and mod_data.dlc ~= nil and mod_data.value == 0 then + Trading:ShowMaskModTradabilityMessage(mod.name_localized) + return false + end + return true + +end + +function Trading:ShowMaskTradabilityMessage(mask_name, items) + + local item_names = "" + for k, v in pairs( items ) do + if item_names ~= "" then + item_names = item_names .. ", and " + end + item_names = (item_names .. "'{1}'"):gsub("{1}", managers.blackmarket:get_mask_mod_name(v, k)) + end + + local title = managers.localization:text("Trading_TradeWindowTitle") + local message = managers.localization:text("Trading_UntradableMask") + message = message:gsub("{1}", mask_name) + message = message:gsub("{2}", item_names) + local menuOptions = {} + menuOptions[1] = { + text = managers.localization:text("Trading_UntradableMaskAccept"), + is_cancel_button = true + } + local tradeMenu = QuickMenu:new(title, message, menuOptions, true) + +end + +function Trading:ShowMaskModTradabilityMessage(mod_name) + + local title = managers.localization:text("Trading_TradeWindowTitle") + local message = managers.localization:text("Trading_UntradableMaskMod") + message = message:gsub("{1}", mod_name) + local menuOptions = {} + menuOptions[1] = { + text = managers.localization:text("Trading_UntradableMaskModAccept"), + is_cancel_button = true + } + local tradeMenu = QuickMenu:new(title, message, menuOptions, true) + +end + +-- Fix for people who traded away their preferred character masks +function Trading:_FixPreferredCharacterMask() + + if GoonBase.Options.Trading and not GoonBase.Options.Trading.FixedPreferredCharacterMask then + + Print("[Trading] Checking if player traded away character locked mask...") + + local index = managers.blackmarket._global.crafted_items.masks[1] + local requires_reset = false + + if index then + if index.mask_id ~= "character_locked" then + requires_reset = true + end + else + requires_reset = true + end + + if requires_reset then + + Print("[Trading] Charcter locked mask not in proper place, fixing, mask in its place will be lost...") + + if not index then + index = {} + end + + index.mask_id = "character_locked" + index.global_value = "normal" + index.modded = false + index.blueprint = { + ["color"] = { + id = "nothing", + global_value = "normal", + }, + ["material"] = { + id = "plastic", + global_value = "normal", + }, + ["pattern"] = { + id = "no_color_no_material", + global_value = "normal", + }, + } + + managers.blackmarket._global.crafted_items.masks[1] = index + + else + Print("[Trading] Mask is in proper place, skipping fix") + end + + GoonBase.Options.Trading.FixedPreferredCharacterMask = true + GoonBase.Options:Save() + + end + +end diff --git a/GoonMod/mods/disabled/train_heist_plans.lua b/GoonMod/mods/disabled/train_heist_plans.lua new file mode 100644 index 0000000..bbbd03c --- /dev/null +++ b/GoonMod/mods/disabled/train_heist_plans.lua @@ -0,0 +1,111 @@ + +-- Mod Definition +local Mod = class( BaseMod ) +Mod.id = "TrainHeistPlans" +Mod.Name = "Separate Train Heist" +Mod.Desc = "The train heist from the Armoured Transport DLC is available as a separate heist.\nWARNING: Will cause problems with people who do not have the mod." +Mod.Requirements = { "ExtendedInventory" } +Mod.Incompatibilities = {} + +Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() + GoonBase.Mods:RegisterMod( Mod ) +end) + +if not Mod:IsEnabled() then + return +end + +Hooks:Add("NarrativeTweakDataInit", "NarrativeTweakDataInit_" .. Mod:ID(), function(data) + + local ExtendedInv = _G.GoonBase.ExtendedInventory + if ExtendedInv == nil then + return + end + + if ExtendedInv:HasItem("train_heist_plans") then + + data.jobs.arm_for_prof = deep_clone( data.jobs.arm_for ) + data.jobs.arm_for_prof.contact = "bain" + data.jobs.arm_for_prof.professional = true + + table.insert( data._jobs_index, "arm_for_prof" ) + table.insert( data.jobs.arm_wrapper.job_wrapper, "arm_for_prof" ) + + data:set_job_wrappers() + + end + +end) + +Hooks:Add("LevelsTweakDataInit", "LevelsTweakDataInit_" .. Mod:ID(), function(data) + + local ExtendedInv = _G.GoonBase.ExtendedInventory + if ExtendedInv == nil then + return + end + + if ExtendedInv:HasItem("train_heist_plans") then + + data.arm_for_prof = deep_clone( data.arm_for ) + data.arm_for_prof.bonus_escape = false + data.arm_for_prof.static_experience = { + 60000, + 70000, + 80000, + 90000, + 100000 + } + + table.insert(data._level_index, "arm_for_prof") + + end + +end) + +Hooks:Add("ExtendedInventoryInitialized", "ExtendedInventoryInitialized_" .. Mod:ID(), function() + + local ExtendedInv = _G.GoonBase.ExtendedInventory + if ExtendedInv == nil then + return + end + + ExtendedInv:RegisterItem({ + id = "train_heist_plans", + name = "TrainHeist_PlansInv", + desc = "TrainHeist_PlansInvDesc", + texture = "guis/dlcs/dlc1/textures/pd2/mission_briefing/assets/train_01", + hide_when_none_in_stock = true, + }) + +end) + +Hooks:Add("JobManagerOnSetNextInteruptStage", "JobManagerOnSetNextInteruptStage_" .. Mod:ID(), function(job_manager, interupt) + + if interupt == "arm_for" then + + managers.job:set_next_interupt_stage(nil) + + local ExtendedInv = _G.GoonBase.ExtendedInventory + if ExtendedInv ~= nil then + ExtendedInv:AddItem("train_heist_plans", 1) + end + + end + +end) + +Hooks:Add("GameStateMachineChangeStateByName", "GameStateMachineChangeStateByName_" .. Mod:ID(), function(gsm, state_name, params) + + if state_name == "victoryscreen" then + + local level_id = Global.game_settings.level_id + if level_id == "arm_for" or level_id == "arm_for_prof" then + local ExtendedInv = _G.GoonBase.ExtendedInventory + if ExtendedInv ~= nil then + ExtendedInv:TakeItem("train_heist_plans", 1) + end + end + + end + +end) diff --git a/GoonMod/mods/disabled/weapon_customization.lua b/GoonMod/mods/disabled/weapon_customization.lua new file mode 100644 index 0000000..5f8a557 --- /dev/null +++ b/GoonMod/mods/disabled/weapon_customization.lua @@ -0,0 +1,616 @@ + +-- Mod Definition +local Mod = class( BaseMod ) +Mod.id = "WeaponCustomization" +Mod.Name = "[BETA] Weapon Customization" +Mod.Desc = "Visually customize your weapons using materials, patterns, and colour swatches" +Mod.Requirements = {} +Mod.Incompatibilities = {} + +Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() + GoonBase.Mods:RegisterMod( Mod ) +end) + +if not Mod:IsEnabled() then + return +end + +-- Weapon Customization +GoonBase.WeaponCustomization = GoonBase.WeaponCustomization or {} +local WeaponCustomization = GoonBase.WeaponCustomization +WeaponCustomization.MenuId = "goonbase_weapon_customization_menu" +WeaponCustomization._update_queue = {} + +WeaponCustomization._default_part_visual_blueprint = { + ["materials"] = "no_material", + ["textures"] = "no_color_no_material", + ["colors"] = "white_solid", +} + +WeaponCustomization._mod_overrides_download_location = "https://github.com/JamesWilko/GoonMod/archive/WeaponCustomizerModOverrides.zip" + +-- Load extras +SafeDoFile( GoonBase.Path .. "mods/weapon_customization_menus.lua" ) +SafeDoFile( GoonBase.Path .. "mods/weapon_customization_part_data.lua" ) + +-- Options +if GoonBase.Options.WeaponCustomization == nil then + GoonBase.Options.WeaponCustomization = {} + GoonBase.Options.WeaponCustomization.Color1R = 1 + GoonBase.Options.WeaponCustomization.Color1G = 1 + GoonBase.Options.WeaponCustomization.Color1B = 1 + GoonBase.Options.WeaponCustomization.Color2R = 1 + GoonBase.Options.WeaponCustomization.Color2G = 1 + GoonBase.Options.WeaponCustomization.Color2B = 1 + GoonBase.Options.WeaponCustomization.Pattern = 1 + GoonBase.Options.WeaponCustomization.Material = 1 + GoonBase.Options.WeaponCustomization.HideDiffuse = false + GoonBase.Options.WeaponCustomization.HideNormal = false + GoonBase.Options.WeaponCustomization.TempShownOverridesNotInstalled = false +end + +-- Menu +Hooks:Add("MenuManagerSetupCustomMenus", "MenuManagerSetupCustomMenus_" .. Mod:ID(), function(menu_manager, menu_nodes) + MenuHelper:NewMenu( WeaponCustomization.MenuId ) +end) + +Hooks:Add("MenuManagerSetupGoonBaseMenu", "MenuManagerSetupGoonBaseMenu_" .. Mod:ID(), function(menu_manager, menu_nodes) + + -- Submenu Button + MenuHelper:AddButton({ + id = "weapon_customization_menu_button", + title = "Options_WeaponCustomizationName", + desc = "Options_WeaponCustomizationDesc", + next_node = WeaponCustomization.MenuId, + menu_id = "goonbase_options_menu", + }) + + -- Menu + MenuCallbackHandler.wc_download_mod_overrides = function(this, item) + if SystemInfo:platform() == Idstring("WIN32") then + os.execute( "explorer " .. WeaponCustomization._mod_overrides_download_location ) + end + end + + MenuCallbackHandler.clear_weapon_visual_customizations = function(this, item) + WeaponCustomization:ShowClearDataConfirmation() + end + + MenuHelper:AddButton({ + id = "weapon_customization_download_mod_overrides", + title = "WeaponCustomization_DownloadModOverridesManual", + desc = "WeaponCustomization_DownloadModOverridesManualDesc", + callback = "wc_download_mod_overrides", + menu_id = WeaponCustomization.MenuId, + priority = 100, + }) + + MenuHelper:AddDivider({ + id = "weapon_customization_divider1", + menu_id = WeaponCustomization.MenuId, + size = 16, + priority = 99, + }) + + MenuHelper:AddButton({ + id = "weapon_customization_clear_data", + title = "WeaponCustomization_ClearDataButton", + desc = "WeaponCustomization_ClearDataButtonDesc", + callback = "clear_weapon_visual_customizations", + menu_id = WeaponCustomization.MenuId, + priority = 98, + }) + +end) + +Hooks:Add("MenuManagerBuildCustomMenus", "MenuManagerBuildCustomMenus_" .. Mod:ID(), function(menu_manager, mainmenu_nodes) + local menu_id = WeaponCustomization.MenuId + local data = { + area_bg = "none" + } + mainmenu_nodes[menu_id] = MenuHelper:BuildMenu( menu_id, data ) +end) + +-- Hooks +Hooks:Add("BlackMarketGUIOnPreviewWeapon", "BlackMarketGUIOnPreviewWeapon_WeaponCustomization", function(gui, data) + if not WeaponCustomization._is_previewing then + WeaponCustomization._is_previewing = { + ["previewing"] = true, + ["data"] = data, + } + end +end) + +Hooks:Add("MenuSceneManagerSpawnedItemWeapon", "MenuSceneManagerSpawnedItemWeapon_" .. Mod:ID(), function(factory_id, blueprint, texture_switches, spawned_unit) + + WeaponCustomization._menu_weapon_preview_unit = spawned_unit + + if WeaponCustomization._is_previewing then + local data = WeaponCustomization._is_previewing["data"] + WeaponCustomization:LoadCurrentWeaponCustomization( data.category, data.slot ) + WeaponCustomization._is_previewing = nil + end + +end) + +Hooks:Add("NewRaycastWeaponBasePostAssemblyComplete", "NewRaycastWeaponBasePostAssemblyComplete_WeaponCustomization", function(weapon, clbk, parts, blueprint) + WeaponCustomization:LoadEquippedWeaponCustomizations( weapon ) +end) + +Hooks:Add("PlayerStandardStartActionEquipWeapon", "PlayerStandardStartActionEquipWeapon_WeaponCustomization", function(ply, t) + if managers.player:local_player() then + WeaponCustomization:LoadEquippedWeaponCustomizations( managers.player:local_player():inventory():equipped_unit():base() ) + end +end) + +Hooks:Add("PlayerStandardStartMaskUp", "PlayerStandardStartMaskUp_WeaponCustomization", function(ply, data) + if managers.player:local_player() then + WeaponCustomization:LoadEquippedWeaponCustomizations( managers.player:local_player():inventory():equipped_unit():base() ) + end +end) + +Hooks:Add("BlackMarketGUIOnPopulateMaskMods", "BlackMarketGUIOnPopulateMaskMods_WeaponCustomization", function(gui, data) + + -- Create "no material" data + if not tweak_data.blackmarket.materials.no_material then + + tweak_data.blackmarket.materials.no_material = {} + tweak_data.blackmarket.materials.no_material.name_id = "bm_mtl_no_material" + tweak_data.blackmarket.materials.no_material.texture = "units/payday2/matcaps/matcap_plastic_df" + tweak_data.blackmarket.materials.no_material.value = 0 + + end + + -- Inject "no material" material + local no_material_index = nil + for k, v in ipairs( data.on_create_data ) do + if v.id == "no_material" then + no_material_index = k + break + end + end + + if no_material_index then + table.remove( data.on_create_data, no_material_index ) + end + + if data.category == "materials" then + + local clear_material = deep_clone( data.on_create_data[1] ) + clear_material.id = "no_material" + clear_material.bitmap_texture_override = "plastic" + clear_material.free_of_charge = true + + table.insert( data.on_create_data, 1, clear_material ) + + end + +end) + +-- Functions +function WeaponCustomization:AddCustomizablePart( part_id ) + local tbl = clone( WeaponCustomization._default_part_visual_blueprint ) + tbl.id = part_id + tbl.modifying = false + table.insert( managers.blackmarket._customizing_weapon_parts, tbl ) +end + +function WeaponCustomization:CreateCustomizablePartsList( weapon ) + + -- Clear weapon parts + managers.blackmarket._customizing_weapon_parts = {} + local blueprint_parts = {} + + -- Add blueprint parts + for k, v in ipairs( weapon.blueprint ) do + + -- Add blueprint part + WeaponCustomization:AddCustomizablePart( v ) + blueprint_parts[v] = true + + -- Check if part has adds + local part_data = tweak_data.weapon.factory.parts[v] + if part_data and part_data.adds then + for x, y in pairs( part_data.adds ) do + WeaponCustomization:AddCustomizablePart( y ) + blueprint_parts[y] = true + end + end + + end + + -- Add weapon extra part adds + local weapon_data = tweak_data.weapon.factory[ weapon.factory_id ] + if weapon_data and weapon_data.adds then + for k, v in pairs( weapon_data.adds ) do + if blueprint_parts[k] then + for x, y in pairs( v ) do + WeaponCustomization:AddCustomizablePart( y ) + end + end + end + end + +end + +function WeaponCustomization:QueueWeaponUpdate( material_id, pattern_id, tint_color_a, tint_color_b, parts_table ) + + if not self._update_queue then + self._update_queue = {} + end + + local data = { + ["material"] = material_id, + ["pattern"] = pattern_id, + ["color_a"] = tint_color_a, + ["color_b"] = tint_color_b, + ["parts"] = parts_table, + } + table.insert( self._update_queue, data ) + +end + +function WeaponCustomization:DequeueUpdate() + + if not self._update_queue then + Print("[Error] Could not dequeue as the update queue does not exist") + return + end + + if #self._update_queue > 0 then + local data = self._update_queue[1] + WeaponCustomization:UpdateWeapon( data["material"], data["pattern"], data["color_a"], data["color_b"], data["parts"] ) + table.remove( self._update_queue, 1 ) + end + +end + +function WeaponCustomization:UpdateWeaponPartsWithMaskMod( data ) + WeaponCustomization:UpdateWeaponPartsWithMod( data.category, data.mods.id ) +end + +function WeaponCustomization:UpdateWeaponPartsWithMod( category, mod_id, parts_table, disable_saving ) + + if managers.blackmarket._customizing_weapon and managers.blackmarket._customizing_weapon_data and managers.blackmarket._selected_weapon_parts then + + -- Set selected part + if managers.blackmarket._selected_weapon_parts[ category ] then + managers.blackmarket._selected_weapon_parts[ category ] = mod_id + end + + -- Get parts to modify + if not parts_table then + parts_table = {} + for k, v in ipairs( managers.blackmarket._customizing_weapon_parts ) do + if v.modifying then + parts_table[v.id] = v + end + end + end + + -- Modify parts + for k, v in pairs( parts_table ) do + + -- Update category mod + if v[ category ] then + v[ category ] = mod_id + end + + -- Update part visuals + local color_data = tweak_data.blackmarket.colors[ v["colors"] ] + WeaponCustomization:UpdateWeapon( v["materials"], v["textures"], color_data.colors[1], color_data.colors[2], { [v.id] = true } ) + + end + + -- Save current weapon customization + if not disable_saving then + WeaponCustomization:SaveCurrentWeaponCustomization() + end + + end + +end + +function WeaponCustomization:UpdateWeaponUsingOptions() + + local opts = GoonBase.Options.WeaponCustomization + local material = WeaponCustomization._materials_lookup[ opts.Material ] + local pattern = WeaponCustomization._patterns_lookup[ opts.Pattern ] + local tint_color_a = Color( opts.Color1R, opts.Color1G, opts.Color1B ) + local tint_color_b = Color( opts.Color2R, opts.Color2G, opts.Color2B ) + + if opts.Pattern == 1 then + if opts.Material == 1 then + pattern = "no_color_no_material" + else + pattern = "no_color_full_material" + end + end + + self:UpdateWeapon( material, pattern, tint_color_a, tint_color_b ) + +end + +function WeaponCustomization:UpdateWeapon( material_id, pattern_id, tint_color_a, tint_color_b, parts_table, unit_override ) + + if self._requesting then + Print("[Error] Could not update weapon customization, already updating current materials") + return + end + + -- Find weapon + local weapon_base = unit_override and unit_override:base() or nil + if not weapon_base then + + if managers.player:local_player() then + weapon_base = managers.player:local_player():inventory():equipped_unit():base() + end + if self._menu_weapon_preview_unit and alive( self._menu_weapon_preview_unit ) then + weapon_base = self._menu_weapon_preview_unit:base() + end + + end + + if not weapon_base then + Print("[Error] Could not update weapon customization, no weapon unit") + return + end + + -- Defaults + material_id = material_id or "no_material" + pattern_id = pattern_id or "no_color_no_material" + if material_id ~= "no_material" and pattern_id == "no_color_no_material" then + pattern_id = "no_color_full_material" + end + if material_id == "no_material" then + pattern_id = "no_color_no_material" + end + tint_color_a = tint_color_a or Color(1, 1, 1) + tint_color_b = tint_color_b or Color(1, 1, 1) + + -- Callbacks + local texture_load_result_clbk = callback(self, self, "clbk_texture_loaded") + + self._materials = {} + self._textures = {} + + -- Find materials + for k, v in pairs( weapon_base._parts ) do + if v.unit and ( (parts_table and parts_table[k]) or not parts_table ) then + + local materials = v.unit:get_objects_by_type(Idstring("material")) + for _, m in ipairs(materials) do + if m:variable_exists(Idstring("tint_color_a")) then + table.insert(self._materials, m) + end + end + + end + end + + -- Material + local old_reflection = self._textures.reflection and self._textures.reflection.name + local material_amount = 1 + if tweak_data.blackmarket.materials[material_id] then + + local material_data = tweak_data.blackmarket.materials[material_id] + local reflection = Idstring(material_data.texture) + if old_reflection ~= reflection then + self._textures.reflection = { + name = reflection, + texture = false, + ready = false + } + end + material_amount = material_data.material_amount or 1 + + end + + -- Pattern + local old_pattern = self._textures.pattern and self._textures.pattern.name + if tweak_data.blackmarket.textures[pattern_id] then + + local pattern = Idstring(tweak_data.blackmarket.textures[pattern_id].texture) + if old_pattern ~= pattern then + self._textures.pattern = { + name = pattern, + texture = false, + ready = false + } + end + + end + + -- Set Textures + for _, material in ipairs(self._materials) do + material:set_variable(Idstring("tint_color_a"), tint_color_a) + material:set_variable(Idstring("tint_color_b"), tint_color_b) + material:set_variable(Idstring("material_amount"), material_amount) + end + + -- Load + self._requesting = true + + for tex_id, texture_data in pairs(self._textures) do + if not texture_data.ready then + texture_data.ready = true + for _, material in ipairs(self._materials) do + Application:set_material_texture(material, Idstring(tex_id == "pattern" and "material_texture" or "reflection_texture"), texture_data.name, Idstring("normal"), 0) + end + end + end + + self._requesting = nil + +end + +function WeaponCustomization:clbk_texture_loaded(tex_name) + + for tex_id, texture_data in pairs(self._textures) do + if not texture_data.ready and tex_name == texture_data.name then + texture_data.ready = true + for _, material in ipairs(self._materials) do + Application:set_material_texture(material, Idstring(tex_id == "pattern" and "material_texture" or "reflection_texture"), tex_name, Idstring("normal"), 0) + end + end + end + +end + +function WeaponCustomization:SaveCurrentWeaponCustomization() + + if not managers.blackmarket._customizing_weapon or not managers.blackmarket._customizing_weapon_data or not managers.blackmarket._selected_weapon_parts then + Print("[Error] Could not save weapon customization, no customization data") + return + end + + -- Get weapon + local weapon_category = managers.blackmarket._customizing_weapon_data.category + local weapon_slot = managers.blackmarket._customizing_weapon_data.slot + local weapon = managers.blackmarket._global.crafted_items[weapon_category][weapon_slot] + + if not weapon then + Print("[Error] Could not save weapon customization, no weapon found in category '", weapon_category, "' slot '", weapon_slot, "'") + return + end + + -- Setup weapon visual customization save + if not weapon.visual_blueprint then + weapon.visual_blueprint = {} + for k, v in ipairs( weapon.blueprint ) do + weapon.visual_blueprint[v] = clone( WeaponCustomization._default_part_visual_blueprint ) + end + end + + -- Get modified parts + local parts = {} + for k, v in ipairs( managers.blackmarket._customizing_weapon_parts ) do + if v.modifying then + parts[v.id] = true + end + end + + -- Update visual customization + for k, v in pairs( parts ) do + + local mat = managers.blackmarket._selected_weapon_parts["materials"] + local tex = managers.blackmarket._selected_weapon_parts["textures"] + local col = managers.blackmarket._selected_weapon_parts["colors"] + + if not weapon.visual_blueprint[k] then + weapon.visual_blueprint[k] = clone( WeaponCustomization._default_part_visual_blueprint ) + end + + weapon.visual_blueprint[k]["materials"] = mat + weapon.visual_blueprint[k]["textures"] = tex + weapon.visual_blueprint[k]["colors"] = col + + end + +end + +function WeaponCustomization:LoadCurrentWeaponCustomization( category, slot ) + + -- Get weapon + local weapon_category = category or managers.blackmarket._customizing_weapon_data.category + local weapon_slot = slot or managers.blackmarket._customizing_weapon_data.slot + if not weapon_category or not weapon_slot then + Print("[Error] Could not load weapon customization, could not find category or slot") + end + local weapon = managers.blackmarket._global.crafted_items[weapon_category][weapon_slot] + + if not weapon then + Print("[Error] Could not load weapon customization, no weapon found in category '", weapon_category, "' slot '", weapon_slot, "'") + return + end + + -- Create default blueprint if it doesn't exist + if not weapon.visual_blueprint then + weapon.visual_blueprint = {} + local weapon = managers.blackmarket._global.crafted_items[category][slot] + for k, v in pairs( weapon.blueprint ) do + weapon.visual_blueprint[v] = clone( WeaponCustomization._default_part_visual_blueprint ) + end + end + + -- Load and apply blueprint + WeaponCustomization:LoadWeaponCustomizationFromBlueprint( weapon.visual_blueprint ) + +end + +function WeaponCustomization:LoadWeaponCustomizationFromBlueprint( blueprint, unit_override ) + + if not blueprint then + blueprint = clone( WeaponCustomization._default_part_visual_blueprint ) + Print("[Warning] Could not load weapon customization, no visual blueprint specified") + return + end + + for k, v in pairs( blueprint ) do + + local material = v["materials"] + local pattern = v["textures"] + local blackmarket_color = v["colors"] + local color = tweak_data.blackmarket.colors[ blackmarket_color ] + + self:UpdateWeapon( material, pattern, color.colors[1], color.colors[2], { [k] = true }, unit_override ) + + end + +end + +function WeaponCustomization:LoadEquippedWeaponCustomizations( weapon_base ) + + local equipped_weapons = { + [1] = managers.blackmarket:equipped_primary(), + [2] = managers.blackmarket:equipped_secondary() + } + + for k, v in pairs( equipped_weapons ) do + if weapon_base._factory_id == v.factory_id and v.visual_blueprint then + WeaponCustomization:LoadWeaponCustomizationFromBlueprint( v.visual_blueprint, weapon_base._unit ) + end + end + +end + +-- Clear Data +function WeaponCustomization:ShowClearDataConfirmation() + + local title = managers.localization:text("WeaponCustomization_ClearDataTitle") + local message = managers.localization:text("WeaponCustomization_ClearDataMessage") + local menuOptions = {} + menuOptions[1] = { + text = managers.localization:text("WeaponCustomization_ClearDataAccept"), + callback = WeaponCustomization.ClearDataFromSave, + is_cancel_button = true + } + menuOptions[2] = { + text = managers.localization:text("WeaponCustomization_ClearDataCancel"), + is_cancel_button = true + } + local menu = QuickMenu:new(title, message, menuOptions, true) + +end + +function WeaponCustomization.ClearDataFromSave() + + if not managers.blackmarket then + return + end + + -- Erase primary weapons + for k, v in pairs( managers.blackmarket._global.crafted_items["primaries"] ) do + if v.visual_blueprint then + v.visual_blueprint = nil + end + end + + -- Erase secondary weapons + for k, v in pairs( managers.blackmarket._global.crafted_items["secondaries"] ) do + if v.visual_blueprint then + v.visual_blueprint = nil + end + end + +end diff --git a/GoonMod/mods/disabled/weapon_customization_menus.lua b/GoonMod/mods/disabled/weapon_customization_menus.lua new file mode 100644 index 0000000..3137f85 --- /dev/null +++ b/GoonMod/mods/disabled/weapon_customization_menus.lua @@ -0,0 +1,756 @@ + +local WeaponCustomization = GoonBase.WeaponCustomization +if not GoonBase.WeaponCustomization then + return +end + +WeaponCustomization._advanced_menu_options = { + [1] = { + text = "wc_adv_toggle_preview_spin", + func = "AdvancedToggleWeaponSpin", + }, + [2] = { + text = "wc_adv_toggle_colour_grading", + func = "AddvancedToggleColourGrading", + }, + [3] = { + text = "wc_adv_clear_weapon", + func = "AdvancedClearWeaponCheck", + } +} +WeaponCustomization._menu_text_scaling = 0.85 + +WeaponCustomization._controller_index = { + modifying = 1, + not_modifying = 1, +} + +local BTN_X = utf8.char(57346) +local BTN_Y = utf8.char(57347) +local BTN_LT = utf8.char(57354) +local BTN_LB = utf8.char(57352) +local BTN_RT = utf8.char(57355) +local BTN_RB = utf8.char(57353) +local BTN_START = utf8.char(57349) + +function WeaponCustomization:IsUsingController() + local type = managers.controller:get_default_wrapper_type() + return type == "xbox360" or type == "ps3" +end + +function WeaponCustomization.weapon_visual_customization_callback(self, data) + + local all_mods_by_type = { + materials = managers.blackmarket:get_inventory_category("materials"), + textures = managers.blackmarket:get_inventory_category("textures"), + colors = managers.blackmarket:get_inventory_category("colors") + } + + local new_node_data = {} + local list = { + "materials", + "textures", + "colors" + } + + local mask_default_blueprint = { + ["materials"] = { + id = "plastic", + global_value = "normal", + }, + ["textures"] = { + id = "no_color_no_material", + global_value = "normal", + }, + ["colors"] = { + id = "nothing", + global_value = "normal", + }, + } + + for _, category in pairs(list) do + + local listed_items = {} + local items = all_mods_by_type[category] + local default = mask_default_blueprint[category] + local mods = {} + + if default then + if default.id == "no_color_no_material" then + default = managers.blackmarket:customize_mask_category_default(category) + end + if default.id ~= "nothing" and default.id ~= "no_color_no_material" then + table.insert(mods, default) + mods[#mods].pcs = {0} + mods[#mods].default = true + listed_items[default.id] = true + end + end + + if category == "materials" and not listed_items.plastic then + table.insert(mods, {id = "plastic", global_value = "normal"}) + mods[#mods].pcs = {0} + mods[#mods].default = true + end + + local td + for i = 1, #items do + td = tweak_data.blackmarket[category][items[i].id] + if not listed_items[items[i].id] and td.texture or td.colors then + table.insert(mods, items[i]) + mods[#mods].pc = td.pc or td.pcs and td.pcs[1] or 10 + mods[#mods].colors = td.colors + end + end + + local sort_td = tweak_data.blackmarket[category] + local x_pc, y_pc + table.sort(mods, function(x, y) + if x.colors and y.colors then + for i = 1, 2 do + local x_color = x.colors[i] + local x_max = math.max(x_color.r, x_color.g, x_color.b) + local x_min = math.min(x_color.r, x_color.g, x_color.b) + local x_diff = x_max - x_min + local x_wl + if x_max == x_min then + x_wl = 10 - x_color.r + elseif x_max == x_color.r then + x_wl = (x_color.g - x_color.b) / x_diff % 6 + elseif x_max == x_color.g then + x_wl = (x_color.b - x_color.r) / x_diff + 2 + elseif x_max == x_color.b then + x_wl = (x_color.r - x_color.g) / x_diff + 4 + end + local y_color = y.colors[i] + local y_max = math.max(y_color.r, y_color.g, y_color.b) + local y_min = math.min(y_color.r, y_color.g, y_color.b) + local y_diff = y_max - y_min + local y_wl + if y_max == y_min then + y_wl = 10 - y_color.r + elseif y_max == y_color.r then + y_wl = (y_color.g - y_color.b) / y_diff % 6 + elseif y_max == y_color.g then + y_wl = (y_color.b - y_color.r) / y_diff + 2 + elseif y_max == y_color.b then + y_wl = (y_color.r - y_color.g) / y_diff + 4 + end + if x_wl ~= y_wl then + return x_wl < y_wl + end + end + end + x_pc = x.pc or x.pcs and x.pcs[1] or 1001 + y_pc = y.pc or y.pcs and y.pcs[1] or 1001 + x_pc = x_pc + (x.global_value and tweak_data.lootdrop.global_values[x.global_value].sort_number or 0) + y_pc = y_pc + (y.global_value and tweak_data.lootdrop.global_values[y.global_value].sort_number or 0) + return x_pc < y_pc + end) + + local max_x = 6 + local max_y = 3 + local mod_data = mods or {} + table.insert(new_node_data, { + name = category, + category = category, + prev_node_data = data, + name_localized = managers.localization:to_upper_text("bm_menu_" .. category), + on_create_func_name = "populate_choose_mask_mod", + on_create_data = mod_data, + override_slots = {max_x, max_y}, + identifier = BlackMarketGui.identifiers.weapon_customization + }) + + end + + new_node_data.topic_id = "bm_menu_customize_weapon_title" + new_node_data.topic_params = { + weapon_name = data.name_localized + } + + local params = {} + params.yes_func = callback(self, self, "_dialog_yes", callback(self, self, "_abort_customized_mask_callback")) + params.no_func = callback(self, self, "_dialog_no") + + new_node_data.back_callback = callback(self, self, "_warn_abort_customized_mask_callback", params) + new_node_data.blur_fade = self._data.blur_fade + new_node_data.weapon_slot_data = data + + if data.category == "primaries" or data.category == "secondaries" then + managers.blackmarket:view_weapon( data.category, data.slot, callback(self, self, "_open_weapon_customization_preview_node", {new_node_data}) ) + end + if data.category == "melee_weapons" then + -- Melee weapons don't work properly yet, so don't uncomment this unless you want to fix it yourself + -- managers.menu:open_node(self._preview_node_name, {}) + -- managers.blackmarket:preview_melee_weapon(data.name) + -- self:_open_weapon_customization_preview_node( {new_node_data} ) + end + +end + +function WeaponCustomization._open_weapon_customization_preview_node(self, data) + + managers.blackmarket._customizing_weapon = true + managers.blackmarket._customizing_weapon_data = data[1].weapon_slot_data + + local category = data[1].weapon_slot_data.category + local slot = data[1].weapon_slot_data.slot + local weapon = managers.blackmarket._global.crafted_items[category][slot] + + WeaponCustomization:CreateCustomizablePartsList( weapon ) + managers.blackmarket._selected_weapon_parts = clone( WeaponCustomization._default_part_visual_blueprint ) + + WeaponCustomization._controller_index = { + modifying = 1, + not_modifying = 1, + } + + managers.menu:open_node("blackmarket_mask_node", data) + WeaponCustomization:LoadCurrentWeaponCustomization( category, slot ) + + WeaponCustomization:Temp_CheckOverridesInstalled() + +end + +Hooks:Add("MenuSceneManagerOverrideSceneTemplate", "MenuSceneManagerOverrideSceneTemplate_WeaponCustomization", function(scene, template, data, custom_name, skip_transition) + if managers.blackmarket._customizing_weapon and template == "blackmarket_mask" then + return "blackmarket_item" + end +end) + +Hooks:Add("BlackMarketGUIStartPageData", "BlackMarketGUIStartPageData_WeaponCustomization", function(gui) + if gui.identifiers then + gui.identifiers.weapon_customization = Idstring("weapon_customization") + end +end) + +Hooks:Add("BlackMarketGUIPostSetup", "BlackMarketGUIPostSetup_WeaponCustomization", function(gui, is_start_page, component_data) + + gui.customize_weapon_visuals = function(gui, data) + WeaponCustomization.weapon_visual_customization_callback(gui, data) + end + gui._open_weapon_customization_preview_node = function(gui, data) + WeaponCustomization._open_weapon_customization_preview_node(gui, data) + end + + local w_visual_customize = { + prio = 5, + btn = "BTN_BACK", + pc_btn = Idstring("toggle_chat"), + name = "WeaponCustomization_MenuItem", + callback = callback(gui, gui, "customize_weapon_visuals") + } + + local btn_x = 10 + gui._btns["w_visual_customize"] = BlackMarketGuiButtonItem:new(gui._buttons, w_visual_customize, btn_x) + +end) + +Hooks:Add("BlackMarketGUIOnPopulateWeaponActionList", "BlackMarketGUIOnPopulateWeaponActionList_WeaponCustomization", function(gui, data) + if data.unlocked then + table.insert(data, "w_visual_customize") + end +end) + +-- Hooks:Add("BlackMarketGUIOnPopulateMeleeWeaponActionList", "BlackMarketGUIOnPopulateMeleeWeaponActionList_WeaponCustomization", function(gui, data) +-- if data.unlocked then +-- table.insert(data, "w_visual_customize") +-- end +-- end) + +Hooks:Add("BlackMarketGUIOnPopulateWeapons", "BlackMarketGUIOnPopulateWeapons_WeaponCustomization", function(gui, category, data) + if managers.blackmarket._customizing_weapon then + managers.blackmarket._customizing_weapon = nil + end +end) + +Hooks:Add("BlackMarketGUIChooseMaskPartCallback", "BlackMarketGUIChooseMaskPartCallback_WeaponCustomization", function(gui, data) + WeaponCustomization:UpdateWeaponPartsWithMaskMod( data ) +end) + +Hooks:Add("BlackMarketGUIUpdateInfoText", "BlackMarketGUIUpdateInfoText_WeaponCustomization", function(self) + + local slot_data = self._slot_data + local tab_data = self._tabs[self._selected]._data + local prev_data = tab_data.prev_node_data + local ids_category = Idstring(slot_data.category) + local identifier = tab_data.identifier + local updated_texts = { + {text = ""}, + {text = ""}, + {text = ""}, + {text = ""}, + {text = ""}, + } + + local id = "none" + for k, v in pairs( self.identifiers ) do + if v == identifier then + id = k + end + end + + if identifier == self.identifiers.weapon_customization then + + local blackmarket = managers.blackmarket + if blackmarket._customizing_weapon and blackmarket._customizing_weapon_data and blackmarket._customizing_weapon_parts then + + local weapon_data = managers.blackmarket._customizing_weapon_data + local category = weapon_data.category + local slot = weapon_data.slot + local weapon = managers.blackmarket._global.crafted_items[category][slot] + + updated_texts[2].text = managers.localization:to_upper_text("wc_modifying_parts") .. "\n" + updated_texts[3].text = managers.localization:to_upper_text("wc_not_modifying_parts") .. "\n" + + if WeaponCustomization:IsUsingController() then + updated_texts[2].text = BTN_LT .. " " .. updated_texts[2].text + updated_texts[3].text = BTN_RT .. " " .. updated_texts[3].text + end + + local num_modifying = 0 + local num_not_modifying = 0 + for k, v in pairs( blackmarket._customizing_weapon_parts ) do + if v and v.modifying then + num_modifying = num_modifying + 1 + else + num_not_modifying = num_not_modifying + 1 + end + end + + local modifying_lines = 0 + local not_modifying_lines = 0 + for k, v in pairs( blackmarket._customizing_weapon_parts ) do + + if v.id then + local part = tweak_data.weapon.factory.parts[v.id] + if part then + + local part_name = WeaponCustomization:_GetLocalizedPartName(v.id, part) + local modifying = (v and v.modifying or false) + local part_index = modifying and 2 or 3 + + if WeaponCustomization:IsUsingController() then + + if v and v.modifying then + modifying_lines = modifying_lines + 1 + else + not_modifying_lines = not_modifying_lines + 1 + end + + -- Clamp values + if WeaponCustomization._controller_index.modifying > num_modifying then + WeaponCustomization._controller_index.modifying = 1 + end + if WeaponCustomization._controller_index.not_modifying > num_not_modifying then + WeaponCustomization._controller_index.not_modifying = 1 + end + + -- Place markers on appropriate lines + local ind = WeaponCustomization:_GetIndexFromLine(modifying and modifying_lines or not_modifying_lines, modifying) + if modifying then + if WeaponCustomization._controller_index.modifying == modifying_lines then + part_name = BTN_X .. " " .. part_name + end + else + if WeaponCustomization._controller_index.not_modifying == not_modifying_lines then + part_name = BTN_Y .. " " .. part_name + end + end + + end + part_name = " " .. part_name .. "\n" + + updated_texts[ part_index ].text = updated_texts[ part_index ].text .. part_name + + end + end + + end + + end + + -- Add advanced options + self._info_texts_color[5] = tweak_data.screen_colors.text + updated_texts[5].text = "\n" + if WeaponCustomization:IsUsingController() then + updated_texts[5].text = updated_texts[5].text .. BTN_START .. " " + end + updated_texts[5].text = updated_texts[5].text .. managers.localization:to_upper_text("wc_advanced_options") .. "\n" + for k, v in ipairs( WeaponCustomization._advanced_menu_options ) do + updated_texts[5].text = updated_texts[5].text .. managers.localization:text( v.text ) .. "\n" + end + + -- Selected Mod + updated_texts[4].text = "\n" + if slot_data then + + updated_texts[4].text = "\n" .. managers.localization:to_upper_text("wc_highlighted_mod") .. "\n" .. slot_data.name_localized + + if not slot_data.unlocked or (type(slot_data.unlocked) == "number" and slot_data.unlocked <= 0) then + self._info_texts_color[5] = tweak_data.screen_colors.important_1 + updated_texts[5].text = managers.localization:text("wc_unavailable_mod") + end + + end + + -- updated_texts[5].text = "" + -- updated_texts[5].text = updated_texts[5].text .. "\n BTN_X: " .. BTN_X + -- updated_texts[5].text = updated_texts[5].text .. "\n BTN_Y: " .. BTN_Y + -- updated_texts[5].text = updated_texts[5].text .. "\n BTN_LB: " .. BTN_LB + -- updated_texts[5].text = updated_texts[5].text .. "\n BTN_LT: " .. BTN_LT + -- updated_texts[5].text = updated_texts[5].text .. "\n BTN_RB: " .. BTN_RB + -- updated_texts[5].text = updated_texts[5].text .. "\n BTN_RT: " .. BTN_RT + + -- Update texts + self:_update_info_text(slot_data, updated_texts, nil, WeaponCustomization._menu_text_scaling) + + end + +end) + +Hooks:Add("BlackMarketGUIMouseReleased", "BlackMarketGUIMouseReleased_WeaponCustomization", function(gui, button, x, y) + + if not managers.blackmarket._customizing_weapon then + return + end + + if button == Idstring("0") then + WeaponCustomization:LeftMouseReleased(gui, button, x, y) + end + + if button == Idstring("1") then + WeaponCustomization:RightMouseReleased(gui, button, x, y) + end + +end) + +Hooks:Add("MenuUpdate", "MenuUpdate_WeaponCustomization", function(t, dt) + + if WeaponCustomization:IsUsingController() and managers.blackmarket._customizing_weapon then + + local controller = managers.menu:get_controller() + local r_trigger = controller:get_input_pressed("primary_attack") + local l_trigger = controller:get_input_pressed("secondary_attack") + local button_x = controller:get_input_pressed("reload") + local button_y = controller:get_input_pressed("switch_weapon") + local start = controller:get_input_pressed("start") + + -- Cycle through modifying and not modifying options + if r_trigger then + WeaponCustomization._controller_index.not_modifying = WeaponCustomization._controller_index.not_modifying + 1 + WeaponCustomization:_UpdateBlackmarketGUI() + end + + if l_trigger then + WeaponCustomization._controller_index.modifying = WeaponCustomization._controller_index.modifying + 1 + WeaponCustomization:_UpdateBlackmarketGUI() + end + + -- Switch modifying and not modifying items + if button_x then + WeaponCustomization:_SwapWeaponPartModifyingStatus(WeaponCustomization._controller_index.modifying, true, true) + end + + if button_y then + WeaponCustomization:_SwapWeaponPartModifyingStatus(WeaponCustomization._controller_index.not_modifying, false, true) + end + + -- Controller advanced options + if start then + WeaponCustomization:ShowControllerAdvancedOptions() + end + + end + +end) + +function WeaponCustomization:_UpdateBlackmarketGUI() + if managers.menu_component and managers.menu_component._blackmarket_gui then + managers.menu_component._blackmarket_gui:update_info_text() + end +end + +function WeaponCustomization:LeftMouseReleased(gui, button, x, y) + WeaponCustomization:LeftMouseReleased_SelectParts(gui, button, x, y) + WeaponCustomization:LeftMouseReleased_Advanced(gui, button, x, y) +end + +function WeaponCustomization:LeftMouseReleased_SelectParts(gui, button, x, y) + + for k, v in pairs( gui._info_texts ) do + if v:inside(x, y) then + + local line = WeaponCustomization:_GetLineFromObjectRect(x, y, v) - 1 + local modifying_item = nil + if k == 2 then modifying_item = true end + if k == 3 then modifying_item = false end + + if modifying_item ~= nil then + WeaponCustomization:_SwapWeaponPartModifyingStatus(line, modifying_item) + gui:update_info_text() + break + end + + end + end + +end + +function WeaponCustomization:LeftMouseReleased_Advanced(gui, button, x, y) + + local v = gui._info_texts[5] + if not v then + return + end + + if v:inside(x, y) then + + local line = WeaponCustomization:_GetLineFromObjectRect(x, y, v) - 2 + local adv_option = self._advanced_menu_options[ line ] + + if adv_option and adv_option.func then + self[ adv_option.func ]() + end + + end + +end + +function WeaponCustomization:RightMouseReleased(gui, button, x, y) + + for k, v in pairs( gui._info_texts ) do + if v:inside(x, y) then + + local line = WeaponCustomization:_GetLineFromObjectRect(x, y, v) - 1 + local modifying_item = nil + if k == 2 then modifying_item = true end + if k == 3 then modifying_item = false end + + if modifying_item ~= nil then + + local tbl_index = WeaponCustomization:_GetIndexFromLine(line, modifying_item) + + if tbl_index and managers.blackmarket._customizing_weapon_parts[ tbl_index ] then + local mod = managers.blackmarket._customizing_weapon_parts[ tbl_index ].modifying + for a, b in ipairs( managers.blackmarket._customizing_weapon_parts ) do + if a ~= tbl_index then + managers.blackmarket._customizing_weapon_parts[a].modifying = not mod + end + end + end + + gui:update_info_text() + break + + end + + end + end + +end + +function WeaponCustomization:_SwapWeaponPartModifyingStatus( line, modifying, update ) + local tbl_index = WeaponCustomization:_GetIndexFromLine(line, modifying) + if tbl_index and managers.blackmarket._customizing_weapon_parts[ tbl_index ] then + managers.blackmarket._customizing_weapon_parts[ tbl_index ].modifying = not managers.blackmarket._customizing_weapon_parts[ tbl_index ].modifying + end + if update then + WeaponCustomization:_UpdateBlackmarketGUI() + end +end + +function WeaponCustomization:_IsInObjectRect(x, y, obj) + local rx, ry, rw, rh = obj:text_rect() + if x >= rx and x <= rx + rw and y >= ry and y <= ry + rh then + return true + end + return false +end + +function WeaponCustomization:_GetLineFromObjectRect(x, y, obj) + local rx, ry, rw, rh = obj:text_rect() + local font_size = tweak_data.menu.pd2_small_font_size * WeaponCustomization._menu_text_scaling + y = y - ry + font_size / 2 + y = y / (font_size * 1.05) + return math.round_with_precision(y, 0) +end + +function WeaponCustomization:_GetIndexFromLine(line, modifying) + + local i = 0 + + for k, v in ipairs( managers.blackmarket._customizing_weapon_parts ) do + if (modifying and v.modifying) or (not modifying and not v.modifying) then + i = i + 1 + if i == line then + return k + end + end + end + + return nil + +end + +-- Clear Weapon Function +function WeaponCustomization:AdvancedToggleWeaponSpin() + + if managers.menu_scene then + if managers.menu_scene._disable_rotate ~= nil then + managers.menu_scene._disable_rotate = not managers.menu_scene._disable_rotate + else + managers.menu_scene._disable_rotate = true + end + end + +end + +function WeaponCustomization:AddvancedToggleColourGrading() + + if managers and managers.environment_controller then + + local self = WeaponCustomization + local grading = "color_payday" + if self._previous_colour_grading then + grading = self._previous_colour_grading + self._previous_colour_grading = nil + else + self._previous_colour_grading = managers.environment_controller:default_color_grading() + end + + managers.environment_controller:set_default_color_grading( grading ) + managers.environment_controller:refresh_render_settings() + + end + +end + +function WeaponCustomization:AdvancedClearWeaponCheck() + + local title = managers.localization:text("wc_clear_weapon_title") + local message = managers.localization:text("wc_clear_weapon_message") + local menuOptions = {} + menuOptions[1] = { + text = managers.localization:text("wc_clear_weapon_accept"), + callback = WeaponCustomization.AdvancedClearWeaponAccept, + is_cancel_button = true + } + menuOptions[2] = { + text = managers.localization:text("wc_clear_weapon_cancel"), + is_cancel_button = true + } + local menu = QuickMenu:new(title, message, menuOptions, true) + +end + +function WeaponCustomization.AdvancedClearWeaponAccept() + + if managers.blackmarket._customizing_weapon_data then + + local category = managers.blackmarket._customizing_weapon_data.category + local slot = managers.blackmarket._customizing_weapon_data.slot + local weapon = managers.blackmarket._global.crafted_items[category][slot] + + -- Rebuild weapon parts list + WeaponCustomization:CreateCustomizablePartsList( weapon ) + + -- Clear selected parts + managers.blackmarket._selected_weapon_parts = clone( WeaponCustomization._default_part_visual_blueprint ) + + -- Save + WeaponCustomization:UpdateWeaponPartsWithMod( nil, nil, managers.blackmarket._customizing_weapon_parts, true ) + + -- Clear data on slot + if weapon.visual_blueprint then + weapon.visual_blueprint = nil + end + + end + +end + +-- Advanced options for controller +function WeaponCustomization:ShowControllerAdvancedOptions() + + local title = managers.localization:text("wc_advanced_options_menu") + local message = "" + local menuOptions = {} + + local i = 1 + for k, v in ipairs( WeaponCustomization._advanced_menu_options ) do + menuOptions[i] = { + text = managers.localization:text( v.text ), + callback = WeaponCustomization[v.func], + is_cancel_button = true + } + i = i + 1 + end + + menuOptions[i] = { + text = managers.localization:text("wc_clear_weapon_cancel"), + is_cancel_button = true + } + + local menu = QuickMenu:new(title, message, menuOptions, true) + +end + +-- Temporary Popup +function WeaponCustomization:Temp_CheckOverridesInstalled() + + local file_name = "assets/mod_overrides/GoonModWeaponCustomizer/units/payday2/weapons/wpn_fps_ass_74_pts/wpn_fps_ass_74_b_standard.material_config" + file_name = Application:base_path() .. file_name + + local f= io.open(file_name, "r") + if f ~= nil then + io.close(f) + else + if GoonBase.Options.WeaponCustomization and not GoonBase.Options.WeaponCustomization.TempShownOverridesNotInstalled then + WeaponCustomization:Temp_ShowOverridesNotInstalledWindow() + end + end + +end + +function WeaponCustomization:Temp_ShowOverridesNotInstalledWindow() + + local title = managers.localization:text("wc_mod_overrides_not_installed_title") + local message = managers.localization:text("wc_mod_overrides_not_installed_desc") + local menuOptions = {} + menuOptions[1] = { + text = managers.localization:text("wc_mod_overrides_not_installed_download"), + callback = WeaponCustomization.Temp_DownloadOverrides, + is_cancel_button = true + } + menuOptions[2] = { + text = managers.localization:text("wc_mod_overrides_not_installed_dont_show"), + callback = WeaponCustomization.Temp_DontShowInFuture, + is_cancel_button = true + } + menuOptions[3] = { + text = managers.localization:text("wc_mod_overrides_not_installed_cancel"), + is_cancel_button = true + } + local menu = QuickMenu:new(title, message, menuOptions, true) + +end + +function WeaponCustomization:Temp_DownloadOverrides() + + if SystemInfo:platform() == Idstring("WIN32") then + os.execute( "explorer " .. WeaponCustomization._mod_overrides_download_location ) + os.execute( "explorer " .. Application:base_path() .. "assets\\mod_overrides\\" ) + end + +end + +function WeaponCustomization:Temp_DontShowInFuture() + + if GoonBase.Options.WeaponCustomization then + GoonBase.Options.WeaponCustomization.TempShownOverridesNotInstalled = true + GoonBase.Options:Save() + end + +end diff --git a/GoonMod/mods/disabled/weapon_customization_part_data.lua b/GoonMod/mods/disabled/weapon_customization_part_data.lua new file mode 100644 index 0000000..82e7387 --- /dev/null +++ b/GoonMod/mods/disabled/weapon_customization_part_data.lua @@ -0,0 +1,377 @@ + +local WeaponCustomization = GoonBase.WeaponCustomization +if not GoonBase.WeaponCustomization then + return +end + +function WeaponCustomization:_GetLocalizedPartName( part_name, part ) + return string.upper( self:GetLocalizedPartName( part_name, part ) ) +end + +function WeaponCustomization:GetLocalizedPartName( part_name, part ) + if part == "nil" then + return "No Name" + end + if WeaponCustomization.MissingPartLocalizations[ part_name ] then + return WeaponCustomization.MissingPartLocalizations[ part_name ] + end + if WeaponCustomization.MissingPartLocalizations[ part.name_id ] then + return WeaponCustomization.MissingPartLocalizations[ part.name_id ] + end + return (part.name_id and managers.localization:text( part.name_id ) or part_name) +end + +local standard_barrel = "Standard Barrel" +local standard_magazine = "Standard Magazine" +local standard_grip = "Standard Grip" +local standard_foregrip = "Standard Foregrip" +local standard_body = "Standard Body" +local standard_stock = "Standard Stock" +local standard_slide = "Standard Slide" +local standard_rail = "Standard Rail" +local standard_sights = "Standard Sights" +local standard_compensator = "Standard Compensator" +local upper_receiver = "Upper Receiver" +local lower_receiver = "Lower Receiver" +local vertical_grip = "Vertical Grip" +local gadget_adapter = "Gadget Adapter" +local optic_adapter = "Optic Adapter" +local stock_adapter = "Stock Adapter" + +WeaponCustomization.MissingPartLocalizations = { + + -- Secondaries + ["wpn_fps_pis_usp_body_standard"] = standard_body, + ["wpn_fps_pis_usp_m_standard"] = standard_magazine, + ["wpn_fps_pis_usp_fl_adapter"] = standard_body, + + ["wpn_fps_pis_g17_body_standard"] = standard_body, + ["wpn_fps_pis_g17_b_standard"] = standard_slide, + ["wpn_fps_pis_g17_m_standard"] = standard_magazine, + + ["wpn_fps_pis_beretta_b_std"] = standard_barrel, + ["wpn_fps_pis_beretta_body_beretta"] = standard_body, + ["wpn_fps_pis_beretta_m_std"] = standard_magazine, + ["wpn_fps_pis_beretta_g_std"] = standard_grip, + ["wpn_fps_pis_beretta_sl_std"] = standard_slide, + ["wpn_fps_pis_beretta_o_std"] = standard_sights, + ["bm_wp_beretta_body_rail"] = standard_rail, + + ["bm_wp_deagle_body_standard"] = standard_body, + ["bm_wp_deagle_b_standard"] = standard_barrel, + ["bm_wp_deagle_g_standard"] = standard_grip, + ["bm_wp_deagle_m_standard"] = standard_magazine, + ["bm_wp_deagle_o_standard_front"] = "Front Sight", + ["bm_wp_deagle_o_standard_front_long"] = "Front Sight", + ["bm_wp_deagle_o_standard_rear"] = "Rear Sight", + + ["bm_wp_c96_body_standard"] = standard_body, + ["bm_wp_c96_b_standard"] = standard_barrel, + ["bm_wp_c96_m_standard"] = standard_magazine, + ["bm_wp_c96_g_standard"] = standard_grip, + + ["bm_wp_g18c_b_standard"] = standard_barrel, + ["bm_wp_g18c_body_frame"] = standard_body, + + ["wpn_fps_pis_g26_body_stardard"] = standard_body, + ["wpn_fps_pis_g26_m_standard"] = standard_magazine, + ["wpn_fps_pis_g26_b_standard"] = standard_barrel, + + ["bm_wp_g22c_body_standard"] = standard_body, + ["bm_wp_g22c_b_standard"] = standard_barrel, + + ["wpn_fps_pis_judge_b_standard"] = standard_barrel, + ["wpn_fps_pis_judge_fl_adapter"] = gadget_adapter, + ["wpn_fps_pis_judge_g_standard"] = standard_grip, + ["wpn_fps_pis_judge_body_standard"] = standard_body, + + ["wpn_fps_upg_vg_ass_smg_verticalgrip"] = vertical_grip, + ["wpn_fps_upg_vg_ass_smg_verticalgrip_vanilla"] = vertical_grip, + + ["bm_wp_akmsu_fg_standard"] = standard_foregrip, + ["bm_wp_akmsu_b_standard"] = standard_barrel, + ["bm_wp_akmsu_body_lowerreceiver"] = lower_receiver, + + ["wpn_fps_smg_thompson_barrel"] = standard_barrel, + ["wpn_fps_smg_thompson_body"] = standard_body, + ["wpn_fps_smg_thompson_drummag"] = "Drum Magazine", + ["wpn_fps_smg_thompson_fl_adapter"] = gadget_adapter, + ["wpn_fps_smg_thompson_foregrip"] = standard_foregrip, + ["wpn_fps_smg_thompson_grip"] = standard_grip, + ["wpn_fps_smg_thompson_ns_standard"] = standard_compensator, + ["wpn_fps_smg_thompson_o_adapter"] = optic_adapter, + ["wpn_fps_smg_thompson_stock"] = standard_stock, + + ["wpn_fps_smg_uzi_b_standard"] = standard_barrel, + ["wpn_fps_smg_uzi_body_standard"] = standard_body, + ["wpn_fps_smg_uzi_fg_standard"] = standard_foregrip, + ["wpn_fps_smg_uzi_g_standard"] = standard_grip, + ["wpn_fps_smg_uzi_m_standard"] = standard_magazine, + ["wpn_fps_smg_uzi_s_unfolded"] = "Unfolded Stock", + + ["bm_wp_mac10_b_dummy"] = "Dummy Body", + ["bm_wp_mac10_body_mac10"] = standard_body, + + ["wpn_fps_smg_sterling_body_standard"] = standard_body, + ["wpn_fps_smg_sterling_o_adapter"] = optic_adapter, + ["wpn_fps_smg_sterling_s_standard"] = standard_stock, + ["wpn_fps_smg_sterling_m_medium"] = standard_magazine, + ["bm_wp_sterling_b_standard"] = standard_barrel, + + ["bm_wp_mp5_body_mp5"] = standard_body, + ["bm_wp_mp5_body_rail"] = standard_rail, + ["bm_wp_mp5_m_std"] = standard_magazine, + + ["bm_wp_mp7_b_standard"] = standard_magazine, + ["bm_wp_mp7_body_standard"] = standard_body, + ["bm_wp_mp7_m_short"] = "Short Magazine", + ["bm_wp_mp7_s_standard"] = standard_stock, + + ["bm_wp_mp9_b_dummy"] = "Dummy Body", + ["bm_wp_mp9_body_mp9"] = standard_body, + + ["bm_wp_p226_b_standard"] = standard_barrel, + ["bm_wp_p226_body_standard"] = standard_body, + ["bm_wp_p226_g_standard"] = standard_grip, + ["bm_wp_p226_m_standard"] = standard_magazine, + ["bm_wp_p226_o_long"] = standard_sights, + ["bm_wp_p226_o_standard"] = standard_sights, + + ["bm_wp_p90_body_p90"] = standard_body, + ["bm_wp_p90_m_std"] = standard_magazine, + + ["bm_wp_pis_rage_o_adapter"] = optic_adapter, + ["bm_wp_rage_b_standard"] = standard_barrel, + ["bm_wp_rage_body_standard"] = standard_body, + ["bm_wp_rage_g_standard"] = standard_grip, + + ["bm_wp_scorpion_m_standard"] = standard_magazine, + ["wpn_fps_smg_scorpion_b_standard"] = standard_barrel, + ["wpn_fps_smg_scorpion_body_standard"] = standard_body, + ["wpn_fps_smg_scorpion_extra_rail"] = standard_rail, + ["wpn_fps_smg_scorpion_extra_rail_gadget"] = "Rail Gadget", + ["wpn_fps_smg_scorpion_g_standard"] = standard_grip, + ["wpn_fps_smg_scorpion_s_standard"] = standard_stock, + + ["wpn_fps_smg_tec9_b_long"] = "Long Barrel", + ["wpn_fps_smg_tec9_body_standard"] = standard_body, + ["wpn_fps_smg_tec9_m_standard"] = standard_magazine, + + ["wpn_fps_pis_ppk_b_standard"] = standard_barrel, + ["wpn_fps_pis_ppk_body_standard"] = standard_body, + ["wpn_fps_pis_ppk_fl_mount"] = gadget_adapter, + ["wpn_fps_pis_ppk_g_standard"] = standard_grip, + ["wpn_fps_pis_ppk_m_standard"] = standard_magazine, + + ["wpn_fps_smg_m45_g_standard"] = standard_grip, + ["wpn_fps_smg_m45_s_standard"] = standard_stock, + ["wpn_fps_smg_m45_m_mag"] = standard_magazine, + ["wpn_fps_smg_m45_b_standard"] = standard_barrel, + ["wpn_fps_smg_m45_body_standard"] = standard_body, + + ["bm_wp_upg_o_marksmansight_front"] = "Front Marksman Sight", + + ["bm_wp_striker_b_standard"] = standard_barrel, + ["bm_wp_striker_body_standard"] = standard_body, + + ["bm_wp_hs2000_body_standard"] = standard_body, + ["bm_wp_hs2000_m_standard"] = standard_magazine, + ["bm_wp_hs2000_sl_standard"] = standard_slide, + + -- Primaries + ["wpn_fps_amcar_uupg_fg_amcar"] = standard_foregrip, + ["wpn_fps_amcar_uupg_body_upperreciever"] = upper_receiver, + ["bm_wp_m4_lower_reciever"] = lower_receiver, + ["bm_wp_m4_g_standard"] = standard_grip, + + ["wpn_fps_ass_fal_body_standard"] = standard_body, + ["wpn_fps_ass_fal_fg_standard"] = standard_foregrip, + ["wpn_fps_ass_fal_g_standard"] = standard_grip, + ["wpn_fps_ass_fal_m_standard"] = standard_magazine, + ["wpn_fps_ass_fal_s_standard"] = standard_stock, + + ["wpn_fps_snp_msr_body_wood"] = "Wooden Body", + ["wpn_fps_snp_msr_m_standard"] = standard_magazine, + ["wpn_fps_snp_msr_b_standard"] = standard_barrel, + + ["wpn_fps_snp_mosin_b_medium"] = "Medium Barrel", + ["wpn_fps_snp_mosin_rail"] = "Nagant Rail", + ["wpn_fps_snp_mosin_body_standard"] = standard_body, + ["wpn_fps_snp_mosin_m_standard"] = standard_magazine, + + ["wpn_fps_snp_r93_b_standard"] = standard_barrel, + ["wpn_fps_snp_r93_body_standard"] = standard_body, + ["wpn_fps_snp_r93_m_std"] = standard_magazine, + + ["wpn_fps_snp_m95_bipod"] = "Bipod", + ["wpn_fps_snp_m95_barrel_standard"] = standard_barrel, + ["wpn_fps_snp_m95_lower_reciever"] = lower_receiver, + ["wpn_fps_snp_m95_upper_reciever"] = upper_receiver, + ["wpn_fps_snp_m95_magazine"] = standard_magazine, + + ["wpn_fps_aug_body_aug"] = standard_body, + ["wpn_fps_aug_m_pmag"] = standard_magazine, + + ["bm_wp_famas_body_standard"] = standard_body, + ["bm_wp_famas_b_standard"] = standard_barrel, + ["bm_wp_famas_g_standard"] = standard_grip, + ["bm_wp_famas_m_standard"] = standard_magazine, + ["bm_wp_famas_o_extra"] = optic_adapter, + + ["wpn_fps_sho_ben_s_collapsable"] = "Collapsable Stock", + ["wpn_fps_sho_ben_b_standard"] = standard_barrel, + ["wpn_fps_sho_ben_body_standard"] = standard_body, + + ["bm_wp_r870_body_standard"] = standard_body, + ["bm_wp_r870_b_long"] = standard_barrel, + ["bm_wp_r870_fg_big"] = standard_foregrip, + ["wpn_fps_shot_r870_b_short"] = "Short Barrel", + + ["bm_wp_ak5_b_std"] = standard_barrel, + ["bm_wp_ak5_body_ak5"] = standard_body, + ["bm_wp_ak5_body_rail"] = standard_rail, + + ["bm_wp_ak_body_lowerreceiver"] = lower_receiver, + ["bm_wp_ak_g_standard"] = standard_grip, + ["bm_wp_ak_m_akm"] = standard_magazine, + ["bm_wp_ak_fg_standard"] = standard_foregrip, + + ["bm_wp_akm_body_upperreceiver"] = "AK Dustcover", + ["bm_wp_akm_b_standard"] = standard_barrel, + + ["bm_wp_74_m_standard"] = standard_magazine, + ["bm_wp_74_b_standard"] = standard_barrel, + + ["bm_wp_ak_fg_standard_gold"] = "Golden Foregrip", + ["bm_wp_ak_m_akm_gold"] = "Golden Magazine", + ["bm_wp_akm_b_standard_gold"] = "Golden Barrel", + ["bm_wp_akm_body_upperreceiver_gold"] = "Golden Upper Receiver", + ["bm_wp_ak_body_lowerreceiver_gold"] = "Golden Lower Receiver", + + ["bm_wp_gre_m79_barrel"] = "Barrel", + ["bm_wp_gre_m79_grenade"] = "Grenade", + ["bm_wp_m79_barrelcatch"] = "Barrel Catch", + ["bm_wp_m79_sight_up"] = "Sight", + + ["bm_wp_g3_b_long"] = standard_barrel, + ["bm_wp_g3_body_lower"] = lower_receiver, + ["bm_wp_g3_body_upper"] = upper_receiver, + ["bm_wp_g3_fg_standard"] = standard_foregrip, + ["bm_wp_g3_m_standard"] = standard_magazine, + + ["bm_wp_g36_body_standard"] = standard_body, + ["bm_wp_g36_body_sl8"] = "Sniper Body", + ["bm_wp_g36_g_standard"] = standard_grip, + ["bm_wp_g36_m_standard"] = standard_magazine, + ["bm_wp_g36_s_standard"] = standard_stock, + + ["bm_wp_galil_body_standard"] = standard_body, + ["bm_wp_galil_fg_standard"] = standard_foregrip, + ["bm_wp_galil_g_standard"] = standard_grip, + ["bm_wp_galil_m_standard"] = standard_magazine, + ["bm_wp_galil_s_standard"] = standard_stock, + + ["wpn_fps_lmg_hk21_b_short"] = "Short Barrel", + ["wpn_fps_lmg_hk21_body_lower"] = lower_receiver, + ["wpn_fps_lmg_hk21_body_upper"] = upper_receiver, + ["wpn_fps_lmg_hk21_fg_long"] = "Long Foregrip", + ["wpn_fps_lmg_hk21_g_standard"] = standard_grip, + ["wpn_fps_lmg_hk21_m_standard"] = standard_magazine, + ["wpn_fps_lmg_hk21_s_standard"] = standard_stock, + + ["bm_wp_huntsman_body_standard"] = standard_body, + + ["bm_wp_ksg_b_standard"] = standard_barrel, + ["bm_wp_ksg_body_standard"] = standard_body, + ["bm_wp_ksg_fg_short"] = standard_foregrip, + ["wpn_fps_sho_ksg_fg_standard"] = standard_foregrip, + ["bm_wp_upg_o_mbus_front"] = "Front Flip-up Sight", + ["bm_wp_upg_o_dd_front"] = "Front Sight", + ["bm_wp_upg_o_dd_rear"] = "Rear Sight", + + ["wpn_fps_ass_l85a2_b_medium"] = "Medium Barrel", + ["wpn_fps_ass_l85a2_body_standard"] = standard_body, + ["wpn_fps_ass_l85a2_g_standard"] = standard_grip, + ["wpn_fps_ass_l85a2_ns_standard"] = standard_compensator, + ["wpn_fps_ass_l85a2_o_standard"] = standard_sights, + ["wpn_fps_ass_l85a2_fg_medium"] = standard_foregrip, + + ["bm_wp_m14_m_standard"] = standard_magazine, + ["bm_wp_m14_b_standard"] = standard_barrel, + ["bm_wp_m14_body_lower"] = lower_receiver, + ["bm_wp_m14_body_upper"] = upper_receiver, + + ["bm_wp_m16_fg_standard"] = standard_foregrip, + ["bm_wp_m16_s_solid"] = standard_stock, + ["bm_wp_m16_os_frontsight"] = standard_sights, + + ["wpn_fps_lmg_m249_b_short"] = "Short Barrel", + ["wpn_fps_lmg_m249_body_standard"] = standard_body, + ["wpn_fps_lmg_m249_fg_standard"] = standard_foregrip, + ["wpn_fps_lmg_m249_m_standard"] = "Box Magazine", + ["wpn_fps_lmg_m249_s_modern"] = "Modern Stock", + ["wpn_fps_lmg_m249_s_para"] = standard_stock, + ["wpn_fps_lmg_m249_upper_reciever"] = upper_receiver, + + ["bm_wp_m4_g_standard"] = standard_grip, + ["bm_wp_m4_lower_reciever"] = lower_receiver, + ["bm_wp_m4_s_adapter"] = stock_adapter, + ["bm_wp_m4_uupg_draghandle"] = "Drag Handle", + ["bm_wp_m4_uupg_fg_rail_ext"] = "Foregrip Rail", + ["bm_wp_m4_upg_b_sd_smr"] = "SMR", + + ["wpn_fps_lmg_mg42_n42"] = standard_compensator, + ["wpn_fps_lmg_mg42_b_mg42"] = standard_barrel, + ["wpn_fps_lmg_mg42_reciever"] = "Receiver and Stock", + + ["wpn_fps_lmg_rpk_b_standard"] = standard_barrel, + ["wpn_fps_lmg_rpk_body_lowerreceiver"] = lower_receiver, + ["wpn_fps_lmg_rpk_fg_wood"] = "Wooden Foregrip", + ["wpn_lmg_rpk_m_drum"] = "Drum Magazine", + ["wpn_fps_lmg_rpk_s_wood"] = "Wooden Stock", + + ["bm_wp_saiga_b_standard"] = standard_barrel, + ["bm_wp_saiga_fg_standard"] = standard_foregrip, + ["bm_wp_saiga_m_5rnd"] = standard_magazine, + + ["bm_wp_saw_b_normal"] = "Blade Guard", + ["bm_wp_saw_body_standard"] = "Saw Body", + ["bm_wp_saw_m_blade"] = "Saw Blade", + + ["bm_wp_scar_b_medium"] = "Medium Barrel", + ["bm_wp_scar_body_standard"] = standard_body, + ["bm_wp_scar_m_standard"] = standard_magazine, + ["bm_wp_scar_ns_short"] = standard_compensator, + ["bm_wp_scar_ns_standard"] = standard_compensator, + ["bm_wp_scar_o_flipups_down"] = standard_sights, + ["bm_wp_scar_o_flipups_up"] = standard_sights, + ["bm_wp_scar_s_standard"] = standard_stock, + + ["wpn_fps_sho_b_spas12_long"] = "Long Barrel", + ["wpn_fps_sho_b_spas12_short"] = "Short Barrel", + ["wpn_fps_sho_body_spas12_standard"] = standard_body, + ["wpn_fps_sho_fg_spas12_standard"] = standard_foregrip, + ["wpn_fps_sho_s_spas12_unfolded"] = standard_stock, + + ["wpn_fps_ass_s552_m_standard"] = standard_magazine, + ["wpn_fps_ass_s552_o_flipup"] = standard_sights, + ["wpn_fps_ass_s552_g_standard"] = standard_grip, + ["wpn_fps_ass_s552_s_m4"] = "M4 Stock", + ["wpn_fps_ass_s552_s_standard"] = standard_stock, + ["wpn_fps_ass_s552_b_standard"] = standard_barrel, + ["wpn_fps_ass_s552_body_standard"] = standard_body, + ["wpn_fps_ass_s552_fg_standard"] = standard_foregrip, + + ["bm_wp_vhs_b_standard"] = standard_barrel, + ["bm_wp_vhs_m"] = standard_magazine, + ["bm_wp_vhs_body"] = standard_body, + ["bm_wp_vhs_o_standard"] = standard_sights, + ["bm_wp_vhs_ns_vhs"] = standard_foregrip, + + ["wpn_fps_shot_shorty_m_extended_short"] = "Extended Magazine", + + ["bm_wp_upg_vg_ass_smg_afg"] = "AFG", + ["bm_wp_upg_vg_ass_smg_stubby"] = "Stubby", + ["wpn_fps_upg_fl_ass_peq15_flashlight"] = "Flashlight", + +} diff --git a/GoonMod/mods/disabled/weapon_remember_gadget.lua b/GoonMod/mods/disabled/weapon_remember_gadget.lua new file mode 100644 index 0000000..7d604af --- /dev/null +++ b/GoonMod/mods/disabled/weapon_remember_gadget.lua @@ -0,0 +1,40 @@ + +-- Mod Definition +local Mod = class( BaseMod ) +Mod.id = "RememberGadgetState" +Mod.Name = "Remember Gadget State" +Mod.Desc = "Your weapon gadget will be in the same state as you left it when you switch weapons" +Mod.Requirements = {} +Mod.Incompatibilities = {} + +Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() + GoonBase.Mods:RegisterMod( Mod ) +end) + +if not Mod:IsEnabled() then + return +end + +-- Data +_G.GoonBase.RememberGadgetState = _G.GoonBase.RememberGadgetState or {} +local Gadget = _G.GoonBase.RememberGadgetState +Gadget.States = {} + +-- Hooks +Hooks:Add("PlayerStandardStartActionEquipWeapon", "PlayerStandardStartActionEquipWeapon_WeaponRememberGadget", function(ply, t) + + local weapon_base = ply._equipped_unit:base() + if weapon_base._has_gadget then + weapon_base:set_gadget_on(Gadget.States[ weapon_base._factory_id ] or 0, true) + end + +end) + +Hooks:Add("PlayerStandardChangingWeapon", "PlayerStandardChangingWeapon_" .. Mod:ID(), function(ply) + + local weapon_base = ply._equipped_unit:base() + if weapon_base._has_gadget then + Gadget.States[ weapon_base._factory_id ] = weapon_base._gadget_on or 0 + end + +end) diff --git a/GoonMod/mods/disabled/zoom_sensitivity.lua b/GoonMod/mods/disabled/zoom_sensitivity.lua new file mode 100644 index 0000000..ed4033c --- /dev/null +++ b/GoonMod/mods/disabled/zoom_sensitivity.lua @@ -0,0 +1,64 @@ + +-- Mod Definition +local Mod = class( BaseMod ) +Mod.id = "IronsightSensitivity" +Mod.Name = "Ironsight Normalized Sensitivity" +Mod.Desc = "Lower the sensitivity when using ironsights to more accurately place your shots" +Mod.Requirements = {} +Mod.Incompatibilities = {} + +Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() + GoonBase.Mods:RegisterMod( Mod ) +end) + +if not Mod:IsEnabled() then + return +end + +-- Options +GoonBase.Options.IronsightSensitivity = GoonBase.Options.IronsightSensitivity or {} +GoonBase.Options.IronsightSensitivity.Enabled = true + +-- Add options to menu +Hooks:Add("MenuManagerSetupGoonBaseMenu", "MenuManagerSetupGoonBaseMenu_" .. Mod:ID(), function( menu_manager ) + + local success, err = pcall(function() + + MenuCallbackHandler.toggle_zoom_sensitivity = function(this, item) + GoonBase.Options.IronsightSensitivity.Enabled = item:value() == "on" and true or false + GoonBase.Options:Save() + end + + MenuHelper:AddToggle({ + id = "toggle_zoom_sensitivity", + title = "OptionsMenu_ZoomSensitivityTitle", + desc = "OptionsMenu_ZoomSensitivityMessage", + callback = "toggle_zoom_sensitivity", + value = GoonBase.Options.IronsightSensitivity.Enabled, + menu_id = "goonbase_options_menu" + }) + + end) + if not success then PrintTable(err) end + +end) + +Hooks:Add( "MenuManagerSetMouseSensitivity", "MenuManagerSetMouseSensitivity_" .. Mod:ID(), function( menu_manager, zoomed ) + + if GoonBase.Options.IronsightSensitivity == nil or not GoonBase.Options.IronsightSensitivity.Enabled then + return + end + + -- Zoom sensitivity calculations by Frankelstner + local sens = managers.user:get_setting("camera_sensitivity") + if zoomed and alive( managers.player:player_unit() ) then + local currentState = managers.player:player_unit():movement():current_state() + if alive(currentState._equipped_unit) then + local fovMul = managers.user:get_setting( "fov_multiplier" ) + sens = sens * ( currentState._equipped_unit:base():zoom() or 65 ) * (fovMul + 1) / 2 / ( 65 * fovMul ) + end + end + menu_manager._controller:get_setup():get_connection("look"):set_multiplier(sens * menu_manager._look_multiplier) + managers.controller:rebind_connections() + +end ) diff --git a/GoonMod/mutators/base_mutator.lua b/GoonMod/mutators/base_mutator.lua new file mode 100644 index 0000000..0bda2f8 --- /dev/null +++ b/GoonMod/mutators/base_mutator.lua @@ -0,0 +1,249 @@ + +if BaseMutator then return end +local Mutators = _G.GoonBase.Mutators + +BaseMutator = class() +BaseMutator.Id = "BaseMutator" +BaseMutator.OptionsName = "Base Mutator" +BaseMutator.OptionsDesc = "The base mutator" +BaseMutator.MenuPrefix = "toggle_mutator_" +BaseMutator.MenuSuffix = "" +BaseMutator.HideInOptionsMenu = false +BaseMutator.AllPlayersRequireMod = false +BaseMutator.Incompatibilities = {} + +BaseMutator._AllPlayersRequirementText = "\nWarning: All players must have GoonMod and Mutators installed for this mutator to work properly!" + +function BaseMutator:ID() + return self.Id +end + +function BaseMutator:IncompatibleMutators() + return self.Incompatibilities +end + +function BaseMutator:DoAllPlayersRequireMod() + return self.AllPlayersRequireMod +end + +function BaseMutator:Setup() + if self:IsEnabled() then + self:_OnEnabled() + end +end + +function BaseMutator:SetupLocalization() + + self.OptionsDescOrig = self.OptionsDesc + if self:DoAllPlayersRequireMod() then + self.OptionsDesc = self.OptionsDesc .. self._AllPlayersRequirementText + end + + if managers.localization then + managers.localization:add_localized_strings({ + [ self:GetName() ] = self.OptionsName, + [ self:GetDesc() ] = self.OptionsDesc, + [ self:GetOriginalDesc() ] = self.OptionsDescOrig, + }) + end + +end + +function BaseMutator:SetupMenu() + + -- Setup callback for mutator + local mutator_menu_name = self.MenuPrefix .. self:ID() .. self.MenuSuffix + MenuCallbackHandler[mutator_menu_name] = function(this, item) + + -- Get mutator state + local enabled = item:value() == "on" and true or false + + -- Check if we're trying to enable an incompatible mutator + if enabled then + local incompatibilities = Mutators:CheckIncompatibilities( self ) + if type(incompatibilities) == "table" then + Mutators:ShowIncompatibilitiesWindow( self, incompatibilities ) + item:set_value("off") + return + end + end + + -- Toggle mutator + self:OnToggled(enabled) + + -- Verify mutators are compatible + Mutators:VerifyAllIncompatibilities() + + end + + -- Add to menu + MenuHelper:AddToggle({ + id = mutator_menu_name, + title = self:GetName(), + desc = self:GetDesc(), + callback = mutator_menu_name, + value = GoonBase.Options.Mutators[ self:ID() ], + disabled_color = Color( 0.5, 1, 0.2, 0.2 ), + menu_id = Mutators.MenuID + }) + +end + +function BaseMutator:GetName() + return "mutator_" .. self.Id .. "_name" +end + +function BaseMutator:GetDesc() + return "mutator_" .. self.Id .. "_desc" +end + +function BaseMutator:GetOriginalDesc() + return "mutator_" .. self.Id .. "_desc_orig" +end + +function BaseMutator:GetLocalizedName() + return managers.localization:text( self:GetName() ) +end + +function BaseMutator:GetLocalizedDesc(ignore_requirement) + local desc = managers.localization:text( self:GetOriginalDesc() ) + if not ignore_requirement and self:DoAllPlayersRequireMod() then + desc = desc .. self._AllPlayersRequirementText + end + return desc +end + +function BaseMutator:IncompatibilitiesLocalizedDesc(incompatibilities) + + local new_str = self:GetLocalizedDesc() + + if incompatibilities ~= nil then + + local str = "" + for k, v in pairs( incompatibilities ) do + local mutator_name = Mutators.LoadedMutators[v]:GetLocalizedName() + if str ~= "" then + str = str .. ", " + end + str = str .. mutator_name + end + new_str = new_str .. " (Incompatible with " .. str .. ")" + + end + + self.OptionsDesc = new_str + managers.localization:add_localized_strings({ [ self:GetDesc() ] = self.OptionsDesc }) + +end + +function BaseMutator:VerifyIncompatibilities(skip_menu_verify) + + local should_disable = false + local incompatible_mutators = {} + + for k, v in pairs( self:IncompatibleMutators() ) do + if ( Mutators.LoadedMutators[v] ~= nil and Mutators.LoadedMutators[v]:ShouldBeEnabled() ) or Mutators.ActiveMutators[v] then + should_disable = true + table.insert( incompatible_mutators, v ) + end + end + + if not skip_menu_verify then + self:MenuSetEnabled( not should_disable ) + self:IncompatibilitiesLocalizedDesc( #incompatible_mutators > 0 and incompatible_mutators or nil ) + end + + return not should_disable + +end + +function BaseMutator:MenuSetEnabled(enabled) + + local menu = MenuHelper:GetMenu( Mutators.MenuID ) + for k, v in pairs( menu["_items"] ) do + local menu_name = v["_parameters"]["name"]:gsub(self.MenuPrefix, "") + if menu_name == self:ID() then + v:set_enabled( enabled ) + v:dirty() + end + end + +end + +function BaseMutator:OnToggled(enabled) + + GoonBase.Options.Mutators = GoonBase.Options.Mutators or {} + GoonBase.Options.Mutators[self.Id] = enabled + GoonBase.Options:Save() + + if enabled then + self:_OnEnabled() + else + self:_OnDisabled() + end + +end + +function BaseMutator:IsEnabled() + + local Net = _G.LuaNetworking + if Net:IsMultiplayer() and not Net:IsHost() then + return false + end + + if Global.game_settings ~= nil then + if Net:IsMultiplayer() and Global.game_settings.permission == "public" then + return false + end + if Global.game_settings.active_mutators then + return Global.game_settings.active_mutators[self.Id] or false + end + end + + return false + +end + +function BaseMutator:ShouldBeEnabled() + + if GoonBase.Options.Mutators == nil then + return false + end + + return GoonBase.Options.Mutators[self.Id] or false + +end + +function BaseMutator:ForceEnable() + return self:_OnEnabled() +end + +function BaseMutator:_OnEnabled() + Mutators.ActiveMutators[self:ID()] = true + self:OnEnabled() + self:_UpdateMatchmaking() +end + +function BaseMutator:OnEnabled() + Print("Base Mutator Enabled") +end + +function BaseMutator:_OnDisabled() + if not Mutators:IsInGame() then + Mutators.ActiveMutators[self:ID()] = nil + end + self:OnDisabled() + self:_UpdateMatchmaking() +end + +function BaseMutator:OnDisabled() + Print("Base Mutator Disabled") +end + +function BaseMutator:_UpdateMatchmaking() + local psuccess, perror = pcall(function() + if MenuCallbackHandler then + MenuCallbackHandler:update_matchmake_attributes() + end + end) +end diff --git a/GoonMod/mutators/mutator_addicts.lua b/GoonMod/mutators/mutator_addicts.lua new file mode 100644 index 0000000..fe8d074 --- /dev/null +++ b/GoonMod/mutators/mutator_addicts.lua @@ -0,0 +1,117 @@ + +local Mutator = class(BaseMutator) +Mutator.Id = "MethAddiction" +Mutator.OptionsName = "Addicts" +Mutator.OptionsDesc = "Players constantly lose health unless they are carrying the bag of drugs" +Mutator.AllPlayersRequireMod = true + +Hooks:Add("GoonBaseRegisterMutators", "GoonBaseRegisterMutators_" .. Mutator:ID(), function() + GoonBase.Mutators:RegisterMutator( Mutator ) +end) + +Mutator._player_lookup = {} +Mutator._player_id_lookup = {} +Mutator._drugs = { + ["coke"] = true, + ["meth"] = true, + ["coke_pure"] = true, + ["sandwich"] = true, +} +Mutator._drug_spawns = { + "coke", + "meth", + "coke_pure", +} +Mutator._addiction_restore = 0.0425 +Mutator._addiction_damage = -0.1250 + +Mutator._gameUpdateHook = "GameSetupUpdate_" .. Mutator:ID() +Mutator._playerDamageOnPostInitHook = "PlayerDamageOnPostInit_" .. Mutator:ID() + +function Mutator:IsDrugsBag( carry_data ) + if not carry_data then + return false + end + return self._drugs[carry_data.carry_id] +end + +function Mutator:ApplyAddictionDamage( unit, carrying_drugs, delta ) + + if unit and unit:character_damage() and unit:character_damage().change_health then + if carrying_drugs then + unit:character_damage():change_health( self._addiction_restore * delta ) + else + unit:character_damage():change_health( self._addiction_damage * delta ) + end + end + +end + +function Mutator:OnEnabled() + + Hooks:Add("GameSetupUpdate", self._gameUpdateHook, function(t, dt) + + local ply_manager = managers.player + if ply_manager then + + local carry_data = ply_manager:get_my_carry_data() + local carrying = carry_data and self:IsDrugsBag(carry_data) + self:ApplyAddictionDamage( ply_manager:player_unit(), carrying, dt ) + + end + + + end) + + Hooks:Add("PlayerDamageOnPostInit", self._playerDamageOnPostInitHook, function(ply, unit) + Queue:Add("AddictsSpawnFreeDrugs", function() + local loot = self._drug_spawns[math.random(1, #self._drug_spawns)] + managers.player:force_verify_carry() + self:SpawnMutatorLoot( loot ) + end, 1) + end) + +end + +function Mutator:OnDisabled() + Hooks:Remove( self._gameUpdateHook ) + Hooks:Remove( self._playerDamageOnPostInitHook ) +end + +function Mutator:SpawnMutatorLoot(loot_id, zipline_unit) + + local psuccess, perror = pcall(function() + + local carry_data = tweak_data.carry[loot_id] + if not carry_data then + return + end + + local player = managers.player:player_unit() + if player then + player:sound():play("Play_bag_generic_throw", nil, false) + else + return + end + + local camera_ext = player:camera() + local dye_initiated = carry_data.dye_initiated + local has_dye_pack = carry_data.has_dye_pack + local dye_value_multiplier = carry_data.dye_value_multiplier + local throw_distance_multiplier_upgrade_level = managers.player:upgrade_level("carry", "throw_distance_multiplier", 0) + + local pos = camera_ext:position() + local rot = camera_ext:rotation() + local peer_id = managers.network:session():local_peer() or 0 + + if not Network:is_client() then + managers.player:server_drop_carry(loot_id, carry_data.multiplier, dye_initiated, has_dye_pack, dye_value_multiplier, pos, rot, player:camera():forward(), throw_distance_multiplier_upgrade_level, zipline_unit, managers.network:session():local_peer():id()) + end + + end) + if not psuccess then + Print("[Error] " .. perror) + end + +end + diff --git a/GoonMod/mutators/mutator_all_bulldozers.lua b/GoonMod/mutators/mutator_all_bulldozers.lua new file mode 100644 index 0000000..6559896 --- /dev/null +++ b/GoonMod/mutators/mutator_all_bulldozers.lua @@ -0,0 +1,157 @@ + +local Mutator = class(BaseMutator) +Mutator.Id = "AllBulldozerSpawns" +Mutator.OptionsName = "Bomb Squad" +Mutator.OptionsDesc = "Replace all spawning units with various Bulldozers and backup" +Mutator.Incompatibilities = { "AllCloakerSpawns", "AllTaserSpawns", "AllShieldSpawns" } + +Mutator.HookTaskData = "GroupAITweakDataPostInitTaskData_AllBulldozerMutator" +Mutator.HookUnitCategories = "GroupAITweakDataPostInitUnitCategories_AllBulldozerMutator" + +Hooks:Add("GoonBaseRegisterMutators", "GoonBaseRegisterMutators_AllBulldozerSpawns", function() + GoonBase.Mutators:RegisterMutator( Mutator ) +end) + +function Mutator:OnEnabled() + + Hooks:Add("GroupAITweakDataPostInitTaskData", self.HookTaskData, function(data, difficulty_index, difficulty) + self:ModifyTaskData(data, difficulty_index, difficulty) + end) + Hooks:Add("GroupAITweakDataPostInitUnitCategories", self.HookUnitCategories, function(data, difficulty_index) + self:ModifyUnitCategories(data, difficulty_index) + end) + +end + +function Mutator:OnDisabled() + Hooks:Remove(self.HookTaskData) + Hooks:Remove(self.HookUnitCategories) +end + +function Mutator:ModifyTaskData(data, difficulty_index, difficulty) + + data.besiege.recurring_group_SO = { + recurring_cloaker_spawn = { + interval = {15, 20}, + retire_delay = 3000 + }, + recurring_spawn_1 = { + interval = {3, 6} + } + } + + data.besiege.assault.groups = { + FBI_swats = { + 0.7, + 0.2, + 0.05 + }, + FBI_heavys = { + 0.7, + 0.2, + 0.05 + }, + FBI_shields = { + 0.7, + 0.2, + 0.05 + }, + FBI_tanks = { + 1, + 0.4, + 0.15 + }, + CS_tazers = { + 0.7, + 0.2, + 0.05 + }, + FBI_spoocs = { + 0.7, + 0.2, + 0.05 + }, + single_spooc = { + 0.7, + 0.2, + 0.05 + } + } + + data.besiege.reenforce.groups = { + CS_defend_a = { + 0.7, + 0.2, + 0.05 + }, + FBI_defend_b = { + 0.7, + 0.2, + 0.05 + }, + FBI_defend_c = { + 0.7, + 0.2, + 0.05 + }, + FBI_defend_d = { + 0.7, + 0.2, + 0.05 + } + } + + data.besiege.recon.groups = { + FBI_stealth_a = { + 0.7, + 0.2, + 0.05 + }, + FBI_stealth_b = { + 0.7, + 0.2, + 0.05 + }, + single_spooc = { + 0.7, + 0.2, + 0.05 + } + } + +end + +function Mutator:ModifyUnitCategories(data, difficulty_index) + + local access_type_walk_only = { walk = true } + + data.special_unit_spawn_limits = { + tank = 1000000, + taser = 0, + spooc = 0, + shield = 0, + } + + data.unit_categories.FBI_tank.units = { + Idstring("units/payday2/characters/ene_bulldozer_1/ene_bulldozer_1"), + Idstring("units/payday2/characters/ene_bulldozer_2/ene_bulldozer_2"), + Idstring("units/payday2/characters/ene_bulldozer_3/ene_bulldozer_3") + } + data.unit_categories.CS_cop_C45_R870 = data.unit_categories.FBI_tank + data.unit_categories.CS_cop_stealth_MP5 = data.unit_categories.FBI_tank + data.unit_categories.CS_swat_MP5 = data.unit_categories.FBI_tank + data.unit_categories.CS_swat_R870 = data.unit_categories.FBI_tank + data.unit_categories.CS_heavy_M4 = data.unit_categories.FBI_tank + data.unit_categories.CS_heavy_M4_w = data.unit_categories.FBI_tank + data.unit_categories.FBI_suit_C45_M4 = data.unit_categories.FBI_tank + data.unit_categories.FBI_suit_M4_MP5 = data.unit_categories.FBI_tank + data.unit_categories.FBI_suit_stealth_MP5 = data.unit_categories.FBI_tank + data.unit_categories.FBI_swat_M4 = data.unit_categories.FBI_tank + data.unit_categories.FBI_swat_R870 = data.unit_categories.FBI_tank + data.unit_categories.FBI_heavy_G36 = data.unit_categories.FBI_tank + data.unit_categories.FBI_heavy_G36_w = data.unit_categories.FBI_tank + data.unit_categories.CS_tazer = data.unit_categories.spooc + data.unit_categories.CS_shield = data.unit_categories.spooc + data.unit_categories.FBI_shield = data.unit_categories.spooc + +end diff --git a/GoonMod/mutators/mutator_all_cloakers.lua b/GoonMod/mutators/mutator_all_cloakers.lua new file mode 100644 index 0000000..702a222 --- /dev/null +++ b/GoonMod/mutators/mutator_all_cloakers.lua @@ -0,0 +1,158 @@ + +local Mutator = class(BaseMutator) +Mutator.Id = "AllCloakerSpawns" +Mutator.OptionsName = "Night of a Million Cloakers" +Mutator.OptionsDesc = "Replace all spawning units with Cloakers" +Mutator.Incompatibilities = { "AllTaserSpawns", "AllBulldozerSpawns", "AllShieldSpawns" } + +Mutator.HookTaskData = "GroupAITweakDataPostInitTaskData_AllCloakersMutator" +Mutator.HookUnitCategories = "GroupAITweakDataPostInitUnitCategories_AllCloakersMutator" + +Hooks:Add("GoonBaseRegisterMutators", "GoonBaseRegisterMutators_AllCloakerSpawns", function() + GoonBase.Mutators:RegisterMutator( Mutator ) +end) + +function Mutator:OnEnabled() + + Hooks:Add("GroupAITweakDataPostInitTaskData", self.HookTaskData, function(data, difficulty_index, difficulty) + self:ModifyTaskData(data, difficulty_index, difficulty) + end) + Hooks:Add("GroupAITweakDataPostInitUnitCategories", self.HookUnitCategories, function(data, difficulty_index) + self:ModifyUnitCategories(data, difficulty_index) + end) + +end + +function Mutator:OnDisabled() + Hooks:Remove(self.HookTaskData) + Hooks:Remove(self.HookUnitCategories) +end + +function Mutator:ModifyTaskData(data, difficulty_index, difficulty) + + data.besiege.recurring_group_SO = { + recurring_cloaker_spawn = { + interval = {0, 1}, + retire_delay = 30 + }, + recurring_spawn_1 = { + interval = {0, 1} + } + } + + data.besiege.assault.groups = { + FBI_swats = { + 1, + 1, + 1 + }, + FBI_heavys = { + 1, + 1, + 1 + }, + FBI_shields = { + 1, + 1, + 1 + }, + FBI_tanks = { + 1, + 1, + 1 + }, + CS_tazers = { + 1, + 1, + 1 + }, + FBI_spoocs = { + 1, + 1, + 1 + }, + single_spooc = { + 1, + 1, + 1 + } + } + + data.besiege.reenforce.groups = { + CS_defend_a = { + 1, + 1, + 1 + }, + FBI_defend_b = { + 1, + 1, + 1 + }, + FBI_defend_c = { + 1, + 1, + 1 + }, + FBI_defend_d = { + 1, + 1, + 1 + } + } + + data.besiege.recon.groups = { + FBI_stealth_a = { + 1, + 1, + 1 + }, + FBI_stealth_b = { + 1, + 1, + 1 + }, + single_spooc = { + 1, + 1, + 1 + } + } + +end + +function Mutator:ModifyUnitCategories(data, difficulty_index) + + local access_type_all = { walk = true, acrobatic = true } + + data.special_unit_spawn_limits = { + tank = 0, + taser = 0, + spooc = 1000000, + shield = 0 + } + + data.unit_categories.spooc.units = { + Idstring("units/payday2/characters/ene_spook_1/ene_spook_1"), + Idstring("units/payday2/characters/ene_spook_1/ene_spook_1"), + Idstring("units/payday2/characters/ene_spook_1/ene_spook_1") + } + data.unit_categories.CS_cop_C45_R870 = data.unit_categories.spooc + data.unit_categories.CS_cop_stealth_MP5 = data.unit_categories.spooc + data.unit_categories.CS_swat_MP5 = data.unit_categories.spooc + data.unit_categories.CS_swat_R870 = data.unit_categories.spooc + data.unit_categories.CS_heavy_M4 = data.unit_categories.spooc + data.unit_categories.CS_heavy_M4_w = data.unit_categories.spooc + data.unit_categories.CS_tazer = data.unit_categories.spooc + data.unit_categories.CS_shield = data.unit_categories.spooc + data.unit_categories.FBI_suit_C45_M4 = data.unit_categories.spooc + data.unit_categories.FBI_suit_M4_MP5 = data.unit_categories.spooc + data.unit_categories.FBI_suit_stealth_MP5 = data.unit_categories.spooc + data.unit_categories.FBI_swat_M4 = data.unit_categories.spooc + data.unit_categories.FBI_swat_R870 = data.unit_categories.spooc + data.unit_categories.FBI_heavy_G36 = data.unit_categories.spooc + data.unit_categories.FBI_heavy_G36_w = data.unit_categories.spooc + data.unit_categories.FBI_shield = data.unit_categories.spooc + data.unit_categories.FBI_tank = data.unit_categories.spooc + +end diff --git a/GoonMod/mutators/mutator_all_gangsters.lua b/GoonMod/mutators/mutator_all_gangsters.lua new file mode 100644 index 0000000..2ef05bd --- /dev/null +++ b/GoonMod/mutators/mutator_all_gangsters.lua @@ -0,0 +1,73 @@ + +local Mutator = class(BaseMutator) +Mutator.Id = "AllGangsterSpawns" +Mutator.OptionsName = "Gangsters Only" +Mutator.OptionsDesc = "Replace all spawning units with various gangsters" +Mutator.Incompatibilities = { "AllCloakerSpawns", "AllBulldozerSpawns", "AllShieldSpawns" } + +Mutator.HookTaskData = "GroupAITweakDataPostInitTaskData_" .. Mutator.Id +Mutator.HookUnitCategories = "GroupAITweakDataPostInitUnitCategories_" .. Mutator.Id + +Hooks:Add("GoonBaseRegisterMutators", "GoonBaseRegisterMutators_" .. Mutator.Id, function() + GoonBase.Mutators:RegisterMutator( Mutator ) +end) + +function Mutator:OnEnabled() + + Hooks:Add("GroupAITweakDataPostInitUnitCategories", self.HookUnitCategories, function(data, difficulty_index) + self:ModifyUnitCategories(data, difficulty_index) + end) + +end + +function Mutator:OnDisabled() + Hooks:Remove(self.HookUnitCategories) +end + +function Mutator:ModifyUnitCategories(data, difficulty_index) + + local gang_mexican = { + Idstring("units/payday2/characters/ene_gang_mexican_1/ene_gang_mexican_1"), + Idstring("units/payday2/characters/ene_gang_mexican_2/ene_gang_mexican_2"), + Idstring("units/payday2/characters/ene_gang_mexican_3/ene_gang_mexican_3"), + Idstring("units/payday2/characters/ene_gang_mexican_4/ene_gang_mexican_4"), + } + local gang_russian = { + Idstring("units/payday2/characters/ene_gang_russian_1/ene_gang_russian_1"), + Idstring("units/payday2/characters/ene_gang_russian_2/ene_gang_russian_2"), + Idstring("units/payday2/characters/ene_gang_russian_3/ene_gang_russian_3"), + Idstring("units/payday2/characters/ene_gang_russian_4/ene_gang_russian_4"), + Idstring("units/payday2/characters/ene_gang_russian_5/ene_gang_russian_5"), + } + local gang_mobster = { + Idstring("units/payday2/characters/ene_gang_mobster_1/ene_gang_mobster_1"), + Idstring("units/payday2/characters/ene_gang_mobster_2/ene_gang_mobster_2"), + Idstring("units/payday2/characters/ene_gang_mobster_3/ene_gang_mobster_3"), + Idstring("units/payday2/characters/ene_gang_mobster_4/ene_gang_mobster_4"), + } + + data.unit_categories.CS_tazer.units = { + gang_mexican[math.random(1, #gang_mexican)], + gang_russian[math.random(1, #gang_russian)], + gang_russian[math.random(1, #gang_russian)], + -- gang_mobster[math.random(1, #gang_mobster)], + -- gang_mobster[math.random(1, #gang_mobster)], + } + data.unit_categories.CS_cop_C45_R870 = data.unit_categories.CS_tazer + data.unit_categories.CS_cop_stealth_MP5 = data.unit_categories.CS_tazer + data.unit_categories.CS_swat_MP5 = data.unit_categories.CS_tazer + data.unit_categories.CS_swat_R870 = data.unit_categories.CS_tazer + data.unit_categories.CS_heavy_M4 = data.unit_categories.CS_tazer + data.unit_categories.CS_heavy_M4_w = data.unit_categories.CS_tazer + data.unit_categories.CS_shield = data.unit_categories.CS_tazer + data.unit_categories.FBI_suit_C45_M4 = data.unit_categories.CS_tazer + data.unit_categories.FBI_suit_M4_MP5 = data.unit_categories.CS_tazer + data.unit_categories.FBI_suit_stealth_MP5 = data.unit_categories.CS_tazer + data.unit_categories.FBI_swat_M4 = data.unit_categories.CS_tazer + data.unit_categories.FBI_swat_R870 = data.unit_categories.CS_tazer + data.unit_categories.FBI_heavy_G36 = data.unit_categories.CS_tazer + data.unit_categories.FBI_heavy_G36_w = data.unit_categories.CS_tazer + data.unit_categories.FBI_shield = data.unit_categories.CS_tazer + data.unit_categories.FBI_tank = data.unit_categories.CS_tazer + +end diff --git a/GoonMod/mutators/mutator_all_shields.lua b/GoonMod/mutators/mutator_all_shields.lua new file mode 100644 index 0000000..aa56244 --- /dev/null +++ b/GoonMod/mutators/mutator_all_shields.lua @@ -0,0 +1,157 @@ + +local Mutator = class(BaseMutator) +Mutator.Id = "AllShieldSpawns" +Mutator.OptionsName = "Spartan Army" +Mutator.OptionsDesc = "Replace all spawning units with Shields" +Mutator.Incompatibilities = { "AllCloakerSpawns", "AllTaserSpawns", "AllBulldozerSpawns" } + +Mutator.HookTaskData = "GroupAITweakDataPostInitTaskData_AllShieldsMutator" +Mutator.HookUnitCategories = "GroupAITweakDataPostInitUnitCategories_AllShieldsMutator" + +Hooks:Add("GoonBaseRegisterMutators", "GoonBaseRegisterMutators_AllShieldSpawns", function() + GoonBase.Mutators:RegisterMutator( Mutator ) +end) + +function Mutator:OnEnabled() + + Hooks:Add("GroupAITweakDataPostInitTaskData", self.HookTaskData, function(data, difficulty_index, difficulty) + self:ModifyTaskData(data, difficulty_index, difficulty) + end) + Hooks:Add("GroupAITweakDataPostInitUnitCategories", self.HookUnitCategories, function(data, difficulty_index) + self:ModifyUnitCategories(data, difficulty_index) + end) + +end + +function Mutator:OnDisabled() + Hooks:Remove(self.HookTaskData) + Hooks:Remove(self.HookUnitCategories) +end + +function Mutator:ModifyTaskData(data, difficulty_index, difficulty) + + data.besiege.recurring_group_SO = { + recurring_cloaker_spawn = { + interval = {0, 1}, + retire_delay = 30 + }, + recurring_spawn_1 = { + interval = {0, 1} + } + } + + data.besiege.assault.groups = { + FBI_swats = { + 1, + 1, + 1 + }, + FBI_heavys = { + 1, + 1, + 1 + }, + FBI_shields = { + 1, + 1, + 1 + }, + FBI_tanks = { + 1, + 1, + 1 + }, + CS_tazers = { + 1, + 1, + 1 + }, + FBI_spoocs = { + 1, + 1, + 1 + }, + single_spooc = { + 1, + 1, + 1 + } + } + + data.besiege.reenforce.groups = { + CS_defend_a = { + 1, + 1, + 1 + }, + FBI_defend_b = { + 1, + 1, + 1 + }, + FBI_defend_c = { + 1, + 1, + 1 + }, + FBI_defend_d = { + 1, + 1, + 1 + } + } + + data.besiege.recon.groups = { + FBI_stealth_a = { + 1, + 1, + 1 + }, + FBI_stealth_b = { + 1, + 1, + 1 + }, + single_spooc = { + 1, + 1, + 1 + } + } + +end + +function Mutator:ModifyUnitCategories(data, difficulty_index) + + local access_type_all = { walk = true, acrobatic = true } + + data.special_unit_spawn_limits = { + tank = 0, + taser = 0, + spooc = 0, + shield = 1000000, + } + + data.unit_categories.FBI_shield.units = { + Idstring("units/payday2/characters/ene_shield_1/ene_shield_1"), + Idstring("units/payday2/characters/ene_shield_1/ene_shield_1"), + Idstring("units/payday2/characters/ene_shield_2/ene_shield_2") + } + data.unit_categories.CS_cop_C45_R870 = data.unit_categories.FBI_shield + data.unit_categories.CS_cop_stealth_MP5 = data.unit_categories.FBI_shield + data.unit_categories.CS_swat_MP5 = data.unit_categories.FBI_shield + data.unit_categories.CS_swat_R870 = data.unit_categories.FBI_shield + data.unit_categories.CS_heavy_M4 = data.unit_categories.FBI_shield + data.unit_categories.CS_heavy_M4_w = data.unit_categories.FBI_shield + data.unit_categories.CS_tazer = data.unit_categories.FBI_shield + data.unit_categories.CS_shield = data.unit_categories.FBI_shield + data.unit_categories.FBI_suit_C45_M4 = data.unit_categories.FBI_shield + data.unit_categories.FBI_suit_M4_MP5 = data.unit_categories.FBI_shield + data.unit_categories.FBI_suit_stealth_MP5 = data.unit_categories.FBI_shield + data.unit_categories.FBI_swat_M4 = data.unit_categories.FBI_shield + data.unit_categories.FBI_swat_R870 = data.unit_categories.FBI_shield + data.unit_categories.FBI_heavy_G36 = data.unit_categories.FBI_shield + data.unit_categories.FBI_heavy_G36_w = data.unit_categories.FBI_shield + data.unit_categories.FBI_tank = data.unit_categories.FBI_shield + +end diff --git a/GoonMod/mutators/mutator_all_tazers.lua b/GoonMod/mutators/mutator_all_tazers.lua new file mode 100644 index 0000000..677c5f7 --- /dev/null +++ b/GoonMod/mutators/mutator_all_tazers.lua @@ -0,0 +1,157 @@ + +local Mutator = class(BaseMutator) +Mutator.Id = "AllTaserSpawns" +Mutator.OptionsName = "Danger, Danger! High Voltage!" +Mutator.OptionsDesc = "Replace all spawning units with Tasers" +Mutator.Incompatibilities = { "AllCloakerSpawns", "AllBulldozerSpawns", "AllShieldSpawns" } + +Mutator.HookTaskData = "GroupAITweakDataPostInitTaskData_AllTasersMutator" +Mutator.HookUnitCategories = "GroupAITweakDataPostInitUnitCategories_AllTasersMutator" + +Hooks:Add("GoonBaseRegisterMutators", "GoonBaseRegisterMutators_AllTaserSpawns", function() + GoonBase.Mutators:RegisterMutator( Mutator ) +end) + +function Mutator:OnEnabled() + + Hooks:Add("GroupAITweakDataPostInitTaskData", self.HookTaskData, function(data, difficulty_index, difficulty) + self:ModifyTaskData(data, difficulty_index, difficulty) + end) + Hooks:Add("GroupAITweakDataPostInitUnitCategories", self.HookUnitCategories, function(data, difficulty_index) + self:ModifyUnitCategories(data, difficulty_index) + end) + +end + +function Mutator:OnDisabled() + Hooks:Remove(self.HookTaskData) + Hooks:Remove(self.HookUnitCategories) +end + +function Mutator:ModifyTaskData(data, difficulty_index, difficulty) + + data.besiege.recurring_group_SO = { + recurring_cloaker_spawn = { + interval = {0, 1}, + retire_delay = 30 + }, + recurring_spawn_1 = { + interval = {0, 1} + } + } + + data.besiege.assault.groups = { + FBI_swats = { + 1, + 1, + 1 + }, + FBI_heavys = { + 1, + 1, + 1 + }, + FBI_shields = { + 1, + 1, + 1 + }, + FBI_tanks = { + 1, + 1, + 1 + }, + CS_tazers = { + 1, + 1, + 1 + }, + FBI_spoocs = { + 1, + 1, + 1 + }, + single_spooc = { + 1, + 1, + 1 + } + } + + data.besiege.reenforce.groups = { + CS_defend_a = { + 1, + 1, + 1 + }, + FBI_defend_b = { + 1, + 1, + 1 + }, + FBI_defend_c = { + 1, + 1, + 1 + }, + FBI_defend_d = { + 1, + 1, + 1 + } + } + + data.besiege.recon.groups = { + FBI_stealth_a = { + 1, + 1, + 1 + }, + FBI_stealth_b = { + 1, + 1, + 1 + }, + single_spooc = { + 1, + 1, + 1 + } + } + +end + +function Mutator:ModifyUnitCategories(data, difficulty_index) + + local access_type_all = { walk = true, acrobatic = true } + + data.special_unit_spawn_limits = { + tank = 0, + taser = 1000000, + spooc = 0, + shield = 0, + } + + data.unit_categories.CS_tazer.units = { + Idstring("units/payday2/characters/ene_tazer_1/ene_tazer_1"), + Idstring("units/payday2/characters/ene_tazer_1/ene_tazer_1"), + Idstring("units/payday2/characters/ene_tazer_1/ene_tazer_1") + } + data.unit_categories.CS_cop_C45_R870 = data.unit_categories.CS_tazer + data.unit_categories.CS_cop_stealth_MP5 = data.unit_categories.CS_tazer + data.unit_categories.CS_swat_MP5 = data.unit_categories.CS_tazer + data.unit_categories.CS_swat_R870 = data.unit_categories.CS_tazer + data.unit_categories.CS_heavy_M4 = data.unit_categories.CS_tazer + data.unit_categories.CS_heavy_M4_w = data.unit_categories.CS_tazer + data.unit_categories.CS_shield = data.unit_categories.CS_tazer + data.unit_categories.FBI_suit_C45_M4 = data.unit_categories.CS_tazer + data.unit_categories.FBI_suit_M4_MP5 = data.unit_categories.CS_tazer + data.unit_categories.FBI_suit_stealth_MP5 = data.unit_categories.CS_tazer + data.unit_categories.FBI_swat_M4 = data.unit_categories.CS_tazer + data.unit_categories.FBI_swat_R870 = data.unit_categories.CS_tazer + data.unit_categories.FBI_heavy_G36 = data.unit_categories.CS_tazer + data.unit_categories.FBI_heavy_G36_w = data.unit_categories.CS_tazer + data.unit_categories.FBI_shield = data.unit_categories.CS_tazer + data.unit_categories.FBI_tank = data.unit_categories.CS_tazer + +end diff --git a/GoonMod/mutators/mutator_exploding_bullets.lua b/GoonMod/mutators/mutator_exploding_bullets.lua new file mode 100644 index 0000000..1ce27e2 --- /dev/null +++ b/GoonMod/mutators/mutator_exploding_bullets.lua @@ -0,0 +1,60 @@ + +local Mutator = class(BaseMutator) +Mutator.Id = "AllBulletsExplode" +Mutator.OptionsName = "4th of July" +Mutator.OptionsDesc = "Every bullet explodes and every person has them" +Mutator.AllPlayersRequireMod = true + +Mutator._WeaponBaseInit = "NewRaycastWeaponBaseInit_" .. Mutator:ID() +Mutator._CopDamage = "CopDamagePreDamageExplosion_" .. Mutator:ID() +Mutator._EnemyDamageMulitplier = 10 + +Hooks:Add("GoonBaseRegisterMutators", "GoonBaseRegisterMutators_" .. Mutator:ID(), function() + GoonBase.Mutators:RegisterMutator( Mutator ) +end) + +function Mutator:OnEnabled() + + Hooks:Add("NewRaycastWeaponBaseInit", self._WeaponBaseInit, function(weapon, unit) + + tweak_data.upgrades.explosive_bullet.curve_pow_orig = tweak_data.upgrades.explosive_bullet.curve_pow + tweak_data.upgrades.explosive_bullet.player_dmg_mul_orig = tweak_data.upgrades.explosive_bullet.player_dmg_mul + tweak_data.upgrades.explosive_bullet.range_orig = tweak_data.upgrades.explosive_bullet.range + + InstantExplosiveBulletBase.CURVE_POW = tweak_data.upgrades.explosive_bullet.curve_pow + InstantExplosiveBulletBase.PLAYER_DMG_MUL = tweak_data.upgrades.explosive_bullet.player_dmg_mul * 4 + InstantExplosiveBulletBase.RANGE = tweak_data.upgrades.explosive_bullet.range * 1.5 + + InstantBulletBase.CURVE_POW = tweak_data.upgrades.explosive_bullet.curve_pow_orig + InstantBulletBase.PLAYER_DMG_MUL = tweak_data.upgrades.explosive_bullet.player_dmg_mul_orig + InstantBulletBase.RANGE = tweak_data.upgrades.explosive_bullet.range_orig + InstantBulletBase.EFFECT_PARAMS = clone( InstantExplosiveBulletBase.EFFECT_PARAMS ) + + InstantBulletBase.on_collision = function(bullet, col_ray, weapon_unit, user_unit, damage, blank) + if damage then + damage = damage * Mutator._EnemyDamageMulitplier + end + InstantExplosiveBulletBase.on_collision(bullet, col_ray, weapon_unit, user_unit, damage, blank) + end + InstantBulletBase.on_collision_client = InstantExplosiveBulletBase.on_collision_client + + end) + + Hooks:Add("CopDamagePreDamageExplosion", self._CopDamage, function(cop, attack_data) + + if attack_data.attacker_unit and attack_data.attacker_unit:movement() then + local same_team = attack_data.attacker_unit:movement():team().id == "law1" + if same_team then + return true + end + end + attack_data.damage = attack_data.damage / Mutator._EnemyDamageMulitplier + + end) + +end + +function Mutator:OnDisabled() + Hooks:Remove(self._WeaponBaseInit) + Hooks:Remove(self._CopDamage) +end diff --git a/GoonMod/mutators/mutator_floating_bodies.lua b/GoonMod/mutators/mutator_floating_bodies.lua new file mode 100644 index 0000000..1c882f5 --- /dev/null +++ b/GoonMod/mutators/mutator_floating_bodies.lua @@ -0,0 +1,76 @@ + +local Mutator = class(BaseMutator) +Mutator.Id = "FloatingBodies" +Mutator.OptionsName = "All Cops go to Heaven" +Mutator.OptionsDesc = "Enemies will float upwards after death. Will not effect bodies with pagers." +Mutator.AllPlayersRequireMod = true + +Mutator._FloatTime = 60 +Mutator._ClientStealthFloatTime = 15 +Mutator._PostUpdateRagdolled = "CopActionHurtPostUpdateRagdolled_" .. Mutator:ID() + +Hooks:Add("GoonBaseRegisterMutators", "GoonBaseRegisterMutators_" .. Mutator:ID(), function() + GoonBase.Mutators:RegisterMutator( Mutator ) +end) + +function Mutator:OnEnabled() + + Hooks:Add("CopActionHurtPostUpdateRagdolled", self._PostUpdateRagdolled, function(cop, t) + + if not cop._death_time then + cop._death_time = t + end + local float_time = t - (cop._death_time or t) + local should_float = float_time < self._FloatTime + local float_timed_out = float_time > self._FloatTime + + if managers.groupai:state():whisper_mode() then + + if Network:is_server() then + if should_float and cop._unit:brain().is_pager_started then + should_float = not cop._unit:brain():is_pager_started() + end + else + should_float = float_time > self._ClientStealthFloatTime + end + + end + + if should_float then + + if not cop._death_twist then + cop._death_twist = math.random(2) == 1 and 1 or -1 + end + + local dt = TimerManager:game():delta_time() + local scale = 1.25 * dt + local unit = cop._unit + local height = 1 + local twist_dir = cop._death_twist + local rot_acc = (math.UP * (0.5 * twist_dir)) * -0.5 + local rot_time = 1 + math.rand(2) + local nr_u_bodies = unit:num_bodies() + local i_u_body = 0 + while nr_u_bodies > i_u_body do + local u_body = unit:body(i_u_body) + if u_body:enabled() and u_body:dynamic() then + local body_mass = u_body:mass() + World:play_physic_effect(Idstring("physic_effects/shotgun_hit"), u_body, math.UP * 600 * scale, 4 * body_mass / math.random(2), rot_acc, rot_time) + end + i_u_body = i_u_body + 1 + end + + end + + if float_timed_out then + cop:_freeze_ragdoll() + cop.update = nil + end + + end) + +end + +function Mutator:OnDisabled() + Hooks:Remove( self._PostUpdateRagdolled ) +end diff --git a/GoonMod/mutators/mutator_insane_spawnrate.lua b/GoonMod/mutators/mutator_insane_spawnrate.lua new file mode 100644 index 0000000..89c0ab0 --- /dev/null +++ b/GoonMod/mutators/mutator_insane_spawnrate.lua @@ -0,0 +1,450 @@ + +local Mutator = class(BaseMutator) +Mutator.Id = "InsaneSpawnRate" +Mutator.OptionsName = "Excessive Force" +Mutator.OptionsDesc = "Increases the number of police that spawn" +Mutator.Incompatibilities = { "SuicidalSpawnRate", "SuicidalSpawnRateCops", "InsaneSpawnRateCops" } + +Mutator.HookSpawnGroups = "GroupAITweakDataPostInitEnemySpawnGroups_InsaneSpawnRate" +Mutator.HookSpawnCategories = "GroupAITweakDataPostInitUnitCategories_InsaneSpawnRate" + +Hooks:Add("GoonBaseRegisterMutators", "GoonBaseRegisterMutators_InsaneSpawnRate", function() + GoonBase.Mutators:RegisterMutator( Mutator ) +end) + +function Mutator:OnEnabled() + + Hooks:Add("GroupAITweakDataPostInitUnitCategories", self.HookSpawnCategories, function(data, difficulty_index) + self:ModifyUnitCategories(data, difficulty_index) + end) + Hooks:Add("GroupAITweakDataPostInitEnemySpawnGroups", self.HookSpawnGroups, function(data, difficulty_index) + self:ModifyTweakData(data, difficulty_index) + end) + +end + +function Mutator:OnDisabled() + Hooks:Remove(self.HookSpawnCategories) + Hooks:Remove(self.HookSpawnGroups) +end + +function Mutator:ModifyUnitCategories(data, difficulty_index) + + data.special_unit_spawn_limits = { + tank = 6, + taser = 18, + spooc = 12, + shield = 64, + } + +end + +function Mutator:ModifyTweakData(data, difficulty_index) + + local self = data + self.enemy_spawn_groups.CS_defend_a = { + amount = {9, 12}, + spawn = { + { + unit = "CS_cop_C45_R870", + freq = 3, + tactics = self._tactics.CS_cop, + rank = 1 + } + } + } + + self.enemy_spawn_groups.CS_defend_b = { + amount = {9, 12}, + spawn = { + { + unit = "CS_swat_MP5", + freq = 3, + amount_min = 3, + tactics = self._tactics.CS_cop, + rank = 1 + } + } + } + + self.enemy_spawn_groups.CS_defend_c = { + amount = {9, 12}, + spawn = { + { + unit = "CS_heavy_M4", + freq = 3, + amount_min = 3, + tactics = self._tactics.CS_cop, + rank = 1 + } + } + } + + self.enemy_spawn_groups.CS_cops = { + amount = {9, 12}, + spawn = { + { + unit = "CS_cop_C45_R870", + freq = 3, + amount_min = 3, + tactics = self._tactics.CS_cop, + rank = 1 + } + } + } + + self.enemy_spawn_groups.CS_stealth_a = { + amount = {6, 9}, + spawn = { + { + unit = "CS_cop_stealth_MP5", + freq = 3, + amount_min = 3, + tactics = self._tactics.CS_cop_stealth, + rank = 1 + } + } + } + + self.enemy_spawn_groups.CS_swats = { + amount = {9, 12}, + spawn = { + { + unit = "CS_swat_MP5", + freq = 3, + tactics = self._tactics.CS_swat_rifle, + rank = 2 + }, + { + unit = "CS_swat_R870", + freq = 1.5, + amount_max = 6, + tactics = self._tactics.CS_swat_shotgun, + rank = 1 + }, + { + unit = "CS_swat_MP5", + freq = 1, + tactics = self._tactics.CS_swat_rifle_flank, + rank = 3 + } + } + } + + self.enemy_spawn_groups.CS_heavys = { + amount = {9, 12}, + spawn = { + { + unit = "CS_heavy_M4", + freq = 3, + tactics = self._tactics.CS_swat_rifle, + rank = 2 + }, + { + unit = "CS_heavy_M4", + freq = 1.15, + tactics = self._tactics.CS_swat_rifle_flank, + rank = 3 + } + } + } + + self.enemy_spawn_groups.CS_shields = { + amount = {9, 12}, + spawn = { + { + unit = "CS_shield", + freq = 3, + amount_min = 3, + amount_max = 6, + tactics = self._tactics.CS_shield, + rank = 3 + }, + { + unit = "CS_cop_stealth_MP5", + freq = 1.5, + amount_max = 3, + tactics = self._tactics.CS_cop_stealth, + rank = 1 + }, + { + unit = "CS_heavy_M4_w", + freq = 2.5, + amount_max = 3, + tactics = self._tactics.CS_swat_heavy, + rank = 2 + } + } + } + + self.enemy_spawn_groups.CS_tazers = { + amount = {3, 9}, + spawn = { + { + unit = "CS_tazer", + freq = 3, + amount_min = 3, + amount_max = 3, + tactics = self._tactics.CS_tazer, + rank = 2 + }, + { + unit = "CS_swat_MP5", + freq = 3, + amount_max = 6, + tactics = self._tactics.CS_cop_stealth, + rank = 1 + } + } + } + + self.enemy_spawn_groups.CS_tanks = { + amount = {3, 6}, + spawn = { + { + unit = "FBI_tank", + freq = 3, + amount_min = 3, + tactics = self._tactics.FBI_tank, + rank = 2 + }, + { + unit = "CS_tazer", + freq = 1.5, + amount_max = 3, + tactics = self._tactics.CS_tazer, + rank = 1 + } + } + } + + self.enemy_spawn_groups.FBI_defend_a = { + amount = {9, 9}, + spawn = { + { + unit = "FBI_suit_C45_M4", + freq = 3, + amount_min = 3, + tactics = self._tactics.FBI_suit, + rank = 2 + }, + { + unit = "CS_cop_C45_R870", + freq = 3, + tactics = self._tactics.FBI_suit, + rank = 1 + } + } + } + + self.enemy_spawn_groups.FBI_defend_b = { + amount = {9, 9}, + spawn = { + { + unit = "FBI_suit_M4_MP5", + freq = 3, + amount_min = 3, + tactics = self._tactics.FBI_suit, + rank = 2 + }, + { + unit = "FBI_swat_M4", + freq = 3, + tactics = self._tactics.FBI_suit, + rank = 1 + } + } + } + + self.enemy_spawn_groups.FBI_defend_c = { + amount = {9, 9}, + spawn = { + { + unit = "FBI_swat_M4", + freq = 3, + tactics = self._tactics.FBI_suit, + rank = 1 + } + } + } + + self.enemy_spawn_groups.FBI_defend_d = { + amount = {6, 9}, + spawn = { + { + unit = "FBI_heavy_G36", + freq = 3, + tactics = self._tactics.FBI_suit, + rank = 1 + } + } + } + + self.enemy_spawn_groups.FBI_stealth_a = { + amount = {6, 9}, + spawn = { + { + unit = "FBI_suit_stealth_MP5", + freq = 3, + amount_min = 3, + tactics = self._tactics.FBI_suit_stealth, + rank = 1 + }, + { + unit = "CS_tazer", + freq = 3, + amount_max = 6, + tactics = self._tactics.CS_tazer, + rank = 2 + } + } + } + + self.enemy_spawn_groups.FBI_stealth_b = { + amount = {6, 9}, + spawn = { + { + unit = "FBI_suit_stealth_MP5", + freq = 3, + amount_min = 3, + tactics = self._tactics.FBI_suit_stealth, + rank = 1 + }, + { + unit = "FBI_suit_M4_MP5", + freq = 2.5, + tactics = self._tactics.FBI_suit, + rank = 2 + } + } + } + + self.enemy_spawn_groups.FBI_swats = { + amount = {9, 12}, + spawn = { + { + unit = "FBI_swat_M4", + freq = 3, + amount_min = 3, + tactics = self._tactics.FBI_swat_rifle, + rank = 2 + }, + { + unit = "FBI_swat_M4", + freq = 2.5, + tactics = self._tactics.FBI_swat_rifle_flank, + rank = 3 + }, + { + unit = "FBI_swat_R870", + freq = 1.5, + amount_max = 6, + tactics = self._tactics.FBI_swat_shotgun, + rank = 1 + }, + { + unit = "spooc", + freq = 0.15, + amount_max = 6, + tactics = self._tactics.spooc, + rank = 1 + } + } + } + + self.enemy_spawn_groups.FBI_heavys = { + amount = {3, 6}, + spawn = { + { + unit = "FBI_heavy_G36", + freq = 3, + tactics = self._tactics.FBI_swat_rifle, + rank = 1 + }, + { + unit = "FBI_heavy_G36", + freq = 2.5, + tactics = self._tactics.FBI_swat_rifle_flank, + rank = 2 + }, + { + unit = "CS_tazer", + freq = 0.75, + amount_max = 3, + tactics = self._tactics.CS_tazer, + rank = 3 + } + } + } + + self.enemy_spawn_groups.FBI_shields = { + amount = {9, 12}, + spawn = { + { + unit = "FBI_shield", + freq = 3, + amount_min = 3, + amount_max = 6, + tactics = self._tactics.FBI_shield_flank, + rank = 3 + }, + { + unit = "CS_tazer", + freq = 2.5, + amount_max = 3, + tactics = self._tactics.CS_tazer, + rank = 2 + }, + { + unit = "FBI_heavy_G36", + freq = 1.5, + amount_max = 3, + tactics = self._tactics.FBI_swat_rifle_flank, + rank = 1 + } + } + } + + self.enemy_spawn_groups.FBI_tanks = { + amount = {6, 8}, + spawn = { + { + unit = "FBI_tank", + freq = 2, + amount_max = 3, + tactics = self._tactics.FBI_tank, + rank = 1 + }, + { + unit = "FBI_shield", + freq = 2, + amount_min = 3, + amount_max = 6, + tactics = self._tactics.FBI_shield_flank, + rank = 3 + }, + { + unit = "FBI_heavy_G36_w", + freq = 2.5, + amount_min = 3, + tactics = self._tactics.FBI_heavy_flank, + rank = 1 + } + } + } + + self.enemy_spawn_groups.single_spooc = { + amount = {2, 2}, + spawn = { + { + unit = "spooc", + freq = 2, + amount_min = 3, + tactics = self._tactics.spooc, + rank = 1 + } + } + } + self.enemy_spawn_groups.FBI_spoocs = self.enemy_spawn_groups.single_spooc + +end diff --git a/GoonMod/mutators/mutator_insane_spawnrate_cops.lua b/GoonMod/mutators/mutator_insane_spawnrate_cops.lua new file mode 100644 index 0000000..f8ee104 --- /dev/null +++ b/GoonMod/mutators/mutator_insane_spawnrate_cops.lua @@ -0,0 +1,293 @@ + +local Mutator = class(BaseMutator) +Mutator.Id = "InsaneSpawnRateCops" +Mutator.OptionsName = "Excessive Force - Cops Only" +Mutator.OptionsDesc = "Increases the number of police that spawn" +Mutator.Incompatibilities = { "SuicidalSpawnRate", "SuicidalSpawnRateCops", "InsaneSpawnRate" } + +Mutator.HookSpawnGroups = "GroupAITweakDataPostInitEnemySpawnGroups_InsaneSpawnRateCops" + +Hooks:Add("GoonBaseRegisterMutators", "GoonBaseRegisterMutators_InsaneSpawnRateCops", function() + GoonBase.Mutators:RegisterMutator( Mutator ) +end) + +function Mutator:OnEnabled() + Hooks:Add("GroupAITweakDataPostInitEnemySpawnGroups", self.HookSpawnGroups, function(data, difficulty_index) + self:ModifyTweakData(data, difficulty_index) + end) +end + +function Mutator:OnDisabled() + Hooks:Remove(self.HookSpawnGroups) +end + +function Mutator:ModifyTweakData(data, difficulty_index) + + local self = data + self.enemy_spawn_groups.CS_defend_a = { + amount = {9, 12}, + spawn = { + { + unit = "CS_cop_C45_R870", + freq = 3, + tactics = self._tactics.CS_cop, + rank = 1 + } + } + } + + self.enemy_spawn_groups.CS_defend_b = { + amount = {9, 12}, + spawn = { + { + unit = "CS_swat_MP5", + freq = 3, + amount_min = 3, + tactics = self._tactics.CS_cop, + rank = 1 + } + } + } + + self.enemy_spawn_groups.CS_defend_c = { + amount = {9, 12}, + spawn = { + { + unit = "CS_heavy_M4", + freq = 3, + amount_min = 3, + tactics = self._tactics.CS_cop, + rank = 1 + } + } + } + + self.enemy_spawn_groups.CS_cops = { + amount = {9, 12}, + spawn = { + { + unit = "CS_cop_C45_R870", + freq = 3, + amount_min = 3, + tactics = self._tactics.CS_cop, + rank = 1 + } + } + } + + self.enemy_spawn_groups.CS_stealth_a = { + amount = {6, 9}, + spawn = { + { + unit = "CS_cop_stealth_MP5", + freq = 3, + amount_min = 3, + tactics = self._tactics.CS_cop_stealth, + rank = 1 + } + } + } + + self.enemy_spawn_groups.CS_swats = { + amount = {9, 12}, + spawn = { + { + unit = "CS_swat_MP5", + freq = 3, + tactics = self._tactics.CS_swat_rifle, + rank = 2 + }, + { + unit = "CS_swat_R870", + freq = 1.5, + amount_max = 6, + tactics = self._tactics.CS_swat_shotgun, + rank = 1 + }, + { + unit = "CS_swat_MP5", + freq = 1, + tactics = self._tactics.CS_swat_rifle_flank, + rank = 3 + } + } + } + + self.enemy_spawn_groups.CS_heavys = { + amount = {9, 12}, + spawn = { + { + unit = "CS_heavy_M4", + freq = 3, + tactics = self._tactics.CS_swat_rifle, + rank = 2 + }, + { + unit = "CS_heavy_M4", + freq = 1.15, + tactics = self._tactics.CS_swat_rifle_flank, + rank = 3 + } + } + } + + self.enemy_spawn_groups.FBI_defend_a = { + amount = {9, 9}, + spawn = { + { + unit = "FBI_suit_C45_M4", + freq = 3, + amount_min = 3, + tactics = self._tactics.FBI_suit, + rank = 2 + }, + { + unit = "CS_cop_C45_R870", + freq = 3, + tactics = self._tactics.FBI_suit, + rank = 1 + } + } + } + + self.enemy_spawn_groups.FBI_defend_b = { + amount = {9, 9}, + spawn = { + { + unit = "FBI_suit_M4_MP5", + freq = 3, + amount_min = 3, + tactics = self._tactics.FBI_suit, + rank = 2 + }, + { + unit = "FBI_swat_M4", + freq = 3, + tactics = self._tactics.FBI_suit, + rank = 1 + } + } + } + + self.enemy_spawn_groups.FBI_defend_c = { + amount = {9, 9}, + spawn = { + { + unit = "FBI_swat_M4", + freq = 3, + tactics = self._tactics.FBI_suit, + rank = 1 + } + } + } + + self.enemy_spawn_groups.FBI_defend_d = { + amount = {6, 9}, + spawn = { + { + unit = "FBI_heavy_G36", + freq = 3, + tactics = self._tactics.FBI_suit, + rank = 1 + } + } + } + + self.enemy_spawn_groups.FBI_stealth_a = { + amount = {6, 9}, + spawn = { + { + unit = "FBI_suit_stealth_MP5", + freq = 3, + amount_min = 3, + tactics = self._tactics.FBI_suit_stealth, + rank = 1 + }, + { + unit = "CS_tazer", + freq = 1, + amount_max = 1, + tactics = self._tactics.CS_tazer, + rank = 2 + } + } + } + + self.enemy_spawn_groups.FBI_stealth_b = { + amount = {6, 9}, + spawn = { + { + unit = "FBI_suit_stealth_MP5", + freq = 3, + amount_min = 3, + tactics = self._tactics.FBI_suit_stealth, + rank = 1 + }, + { + unit = "FBI_suit_M4_MP5", + freq = 2.5, + tactics = self._tactics.FBI_suit, + rank = 2 + } + } + } + + self.enemy_spawn_groups.FBI_swats = { + amount = {9, 12}, + spawn = { + { + unit = "FBI_swat_M4", + freq = 3, + amount_min = 3, + tactics = self._tactics.FBI_swat_rifle, + rank = 2 + }, + { + unit = "FBI_swat_M4", + freq = 2.5, + tactics = self._tactics.FBI_swat_rifle_flank, + rank = 3 + }, + { + unit = "FBI_swat_R870", + freq = 1.5, + amount_max = 6, + tactics = self._tactics.FBI_swat_shotgun, + rank = 1 + }, + { + unit = "spooc", + freq = 0.15, + amount_max = 1, + tactics = self._tactics.spooc, + rank = 1 + } + } + } + + self.enemy_spawn_groups.FBI_heavys = { + amount = {3, 6}, + spawn = { + { + unit = "FBI_heavy_G36", + freq = 3, + tactics = self._tactics.FBI_swat_rifle, + rank = 1 + }, + { + unit = "FBI_heavy_G36", + freq = 2.5, + tactics = self._tactics.FBI_swat_rifle_flank, + rank = 2 + }, + { + unit = "CS_tazer", + freq = 0.75, + amount_max = 1, + tactics = self._tactics.CS_tazer, + rank = 3 + } + } + } + +end diff --git a/GoonMod/mutators/mutator_instagib.lua b/GoonMod/mutators/mutator_instagib.lua new file mode 100644 index 0000000..4af2b04 --- /dev/null +++ b/GoonMod/mutators/mutator_instagib.lua @@ -0,0 +1,38 @@ + +local Mutator = class(BaseMutator) +Mutator.Id = "Instagib" +Mutator.OptionsName = "Instagib" +Mutator.OptionsDesc = "All weapon damage is amplified to lethal levels" +Mutator.AllPlayersRequireMod = true + +Mutator._RWChkID = "NewRaycastWeaponBase_" .. Mutator.Id +Mutator._RWChkIDPost = "NewRaycastWeaponBase_PostHook_" .. Mutator.Id +Mutator._NPChkID = "NPCRaycastWeaponBase_" .. Mutator.Id + +Hooks:Add("GoonBaseRegisterMutators", "GoonBaseRegisterMutators_" .. Mutator.Id, function() + GoonBase.Mutators:RegisterMutator( Mutator ) +end) + +function Mutator:OnEnabled() + + Hooks:Add("NewRaycastWeaponBaseInit", self._RWChkID, function(weapon, unit) + + Hooks:PostHook(NewRaycastWeaponBase, "_get_current_damage", self._RWChkIDPost, function(self, dmg_mul) + return math.huge + end) + + end) + + Hooks:Add("NPCRaycastWeaponBaseInit", self._NPChkID, function(weapon, unit) + weapon._damage = math.huge + end) + +end + +function Mutator:OnDisabled() + + Hooks:Remove(self._RWChkID) + Hooks:RemovePostHook(self._RWChkIDPost) + Hooks:Remove(self._NPChkID) + +end diff --git a/GoonMod/mutators/mutator_jamming_weapons.lua b/GoonMod/mutators/mutator_jamming_weapons.lua new file mode 100644 index 0000000..e260915 --- /dev/null +++ b/GoonMod/mutators/mutator_jamming_weapons.lua @@ -0,0 +1,37 @@ + +local Mutator = class(BaseMutator) +Mutator.Id = "WeaponsJam" +Mutator.OptionsName = "Crappy Weapons" +Mutator.OptionsDesc = "You thought the drills were bad? Weapons randomly jam when firing!" +Mutator.AllPlayersRequireMod = true + +Mutator._WeaponFirehkID = "NewRaycastWeaponBase_" .. Mutator.Id +Mutator._WeaponFirehkIDPost = "NewRaycastWeaponBase_Post_" .. Mutator.Id + +Hooks:Add("GoonBaseRegisterMutators", "GoonBaseRegisterMutators_" .. Mutator.Id, function() + GoonBase.Mutators:RegisterMutator( Mutator ) +end) + +function Mutator:OnEnabled() + + Hooks:Add("NewRaycastWeaponBaseInit", self._WeaponFirehkID, function(weapon, unit) + + Hooks:PostHook(NewRaycastWeaponBase, "fire", self._WeaponFirehkIDPost, function(weapon, from_pos, direction, dmg_mul, shoot_player, spread_mul, autohit_mul, suppr_mul, target_unit) + local roll = math.rand(1) + local chance = 0.12 + local gun = weapon.parent_weapon and weapon.parent_weapon:base() or weapon + if roll < chance then + gun:set_ammo_remaining_in_clip(0) + --one day we'll mosin ping sound but until then, EXPLOSIONSSSSSSSSSS + managers.explosion:play_sound_and_effects(weapon._unit:get_object(Idstring("a_shell")):position(), Vector3(), 0) + end + end) + + end) + +end + +function Mutator:OnDisabled() + Hooks:Remove(self._WeaponFirehkID) + Hooks:RemovePostHook(self._WeaponFirehkIDPost) +end diff --git a/GoonMod/mutators/mutator_lightning_speed.lua b/GoonMod/mutators/mutator_lightning_speed.lua new file mode 100644 index 0000000..6e79127 --- /dev/null +++ b/GoonMod/mutators/mutator_lightning_speed.lua @@ -0,0 +1,130 @@ + +local Mutator = class(BaseMutator) +Mutator.Id = "AllLightningSpeed" +Mutator.OptionsName = "Blitzkrieg" +Mutator.OptionsDesc = "All police units move lightning fast" + +Hooks:Add("GoonBaseRegisterMutators", "GoonBaseRegisterMutators_AllLightningSpeed", function() + GoonBase.Mutators:RegisterMutator( Mutator ) +end) + +Mutator.Units = {} +Mutator.Units.Cop = "CharacterTweakDataPostInitCop_" .. Mutator:ID() +Mutator.Units.FBI = "CharacterTweakDataPostInitFBI_" .. Mutator:ID() +Mutator.Units.SWAT = "CharacterTweakDataPostInitSWAT_" .. Mutator:ID() +Mutator.Units.HeavySWAT = "CharacterTweakDataPostInitHeavySWAT_" .. Mutator:ID() +Mutator.Units.FBISWAT = "CharacterTweakDataPostInitFBISWAT_" .. Mutator:ID() +Mutator.Units.FBIHeavySWAT = "CharacterTweakDataPostInitFBIHeavySWAT_" .. Mutator:ID() +Mutator.Units.CitySWAT = "CharacterTweakDataPostInitCitySWAT_" .. Mutator:ID() +Mutator.Units.Shield = "CharacterTweakDataPostInitShield_" .. Mutator:ID() +Mutator.Units.Taser = "CharacterTweakDataPostInitTaser_" .. Mutator:ID() +Mutator.Units.Tank = "CharacterTweakDataPostInitTank_" .. Mutator:ID() +Mutator.Units.GeneralSpeed = "CharacterTweakDataPostMultiplyAllSpeeds_" .. Mutator:ID() +Mutator.Units.SpawnCategories = "GroupAITweakDataPostInitUnitCategories_" .. Mutator:ID() + +function Mutator:OnEnabled() + + Hooks:Add("CharacterTweakDataPostInitCop", self.Units.Cop, function(data, presets) + data.cop.move_speed = presets.move_speed.lightning + end) + + Hooks:Add("CharacterTweakDataPostInitFBI", self.Units.FBI, function(data, presets) + data.fbi.move_speed = presets.move_speed.lightning + end) + + Hooks:Add("CharacterTweakDataPostInitSWAT", self.Units.SWAT, function(data, presets) + data.swat.move_speed = presets.move_speed.lightning + end) + + Hooks:Add("CharacterTweakDataPostInitHeavySWAT", self.Units.HeavySWAT, function(data, presets) + data.heavy_swat.move_speed = presets.move_speed.lightning + end) + + Hooks:Add("CharacterTweakDataPostInitFBISWAT", self.Units.FBISWAT, function(data, presets) + data.fbi_swat.move_speed = presets.move_speed.lightning + end) + + Hooks:Add("CharacterTweakDataPostInitFBIHeavySWAT", self.Units.FBIHeavySWAT, function(data, presets) + data.fbi_heavy_swat.move_speed = presets.move_speed.lightning + end) + + Hooks:Add("CharacterTweakDataPostInitCitySWAT", self.Units.CitySWAT, function(data, presets) + data.city_swat.move_speed = presets.move_speed.lightning + end) + + Hooks:Add("CharacterTweakDataPostInitShield", self.Units.Shield, function(data, presets) + data.shield.move_speed = presets.move_speed.lightning + end) + + Hooks:Add("CharacterTweakDataPostInitTaser", self.Units.Taser, function(data, presets) + data.taser.move_speed = presets.move_speed.lightning + end) + + Hooks:Add("CharacterTweakDataPostInitTank", self.Units.Tank, function(data, presets) + data.tank.move_speed = presets.move_speed.lightning + end) + + Hooks:Add("CharacterTweakDataPostMultiplyAllSpeeds", self.Units.GeneralSpeed, function(data, walk_mul, run_mul) + + walk_mul = 3 + run_mul = 5 + + local self = data + local all_units = { + "security", + "cop", + "fbi", + "swat", + "heavy_swat", + "sniper", + "gangster", + "tank", + "spooc", + "shield", + "taser" + } + for _, name in ipairs(all_units) do + local speed_table = self[name].SPEED_WALK + speed_table.hos = speed_table.hos * walk_mul + speed_table.cbt = speed_table.cbt * walk_mul + end + self.security.SPEED_RUN = self.security.SPEED_RUN * run_mul + self.cop.SPEED_RUN = self.cop.SPEED_RUN * run_mul + self.fbi.SPEED_RUN = self.fbi.SPEED_RUN * run_mul + self.swat.SPEED_RUN = self.swat.SPEED_RUN * run_mul + self.heavy_swat.SPEED_RUN = self.heavy_swat.SPEED_RUN * run_mul + self.fbi_heavy_swat.SPEED_RUN = self.fbi_heavy_swat.SPEED_RUN * run_mul + self.sniper.SPEED_RUN = self.sniper.SPEED_RUN * run_mul + self.gangster.SPEED_RUN = self.gangster.SPEED_RUN * run_mul + self.tank.SPEED_RUN = self.tank.SPEED_RUN * run_mul + self.spooc.SPEED_RUN = self.spooc.SPEED_RUN * run_mul + self.shield.SPEED_RUN = self.shield.SPEED_RUN * run_mul + self.taser.SPEED_RUN = self.taser.SPEED_RUN * run_mul + self.biker_escape.SPEED_RUN = self.biker_escape.SPEED_RUN * run_mul + + end) + + Hooks:Add("GroupAITweakDataPostInitUnitCategories", self.Units.SpawnCategories, function(data, difficulty_index) + local access_type_all = {walk = true, acrobatic = true} + data.unit_categories.FBI_shield.access = access_type_all + data.unit_categories.FBI_tank.access = access_type_all + data.unit_categories.CS_tazer.access = access_type_all + data.unit_categories.CS_shield.access = access_type_all + end) + +end + +function Mutator:OnDisabled() + Hooks:Remove(self.Units.Cop) + Hooks:Remove(self.Units.FBI) + Hooks:Remove(self.Units.SWAT) + Hooks:Remove(self.Units.HeavySWAT) + Hooks:Remove(self.Units.FBISWAT) + Hooks:Remove(self.Units.FBIHeavySWAT) + Hooks:Remove(self.Units.CitySWAT) + Hooks:Remove(self.Units.Shield) + Hooks:Remove(self.Units.Taser) + Hooks:Remove(self.Units.Tank) + Hooks:Remove(self.Units.GeneralSpeed) + Hooks:Remove(self.Units.SpawnCategories) +end diff --git a/GoonMod/mutators/mutator_no_ammo_pickups.lua b/GoonMod/mutators/mutator_no_ammo_pickups.lua new file mode 100644 index 0000000..e8af9d7 --- /dev/null +++ b/GoonMod/mutators/mutator_no_ammo_pickups.lua @@ -0,0 +1,69 @@ + +local Mutator = class(BaseMutator) +Mutator.Id = "NoAmmoPickups" +Mutator.OptionsName = "Rationing" +Mutator.OptionsDesc = "Enemies no longer drop ammo. Bulldozers have a chance to drop an ammo bag where they die." +Mutator.AllPlayersRequireMod = true + +Mutator.CopDamageInit = "CopDamagePostInitialize_" .. Mutator:ID() +Mutator.CopPostDeath = "CopDamagePostDeath_" .. Mutator:ID() + +Mutator.DozersKilledSinceAmmo = 0 +Mutator.DropAmmoChance = { + [1] = 0.08, + [2] = 0.15, + [3] = 0.30, + [4] = 0.65, + [5] = 1.00, +} + +Hooks:Add("GoonBaseRegisterMutators", "GoonBaseRegisterMutators_" .. Mutator:ID(), function() + GoonBase.Mutators:RegisterMutator( Mutator ) +end) + +function Mutator:OnEnabled() + + Hooks:Add("CopDamagePostInitialize", self.CopDamageInit, function(cop, unit) + cop:set_pickup(nil) + end) + + Hooks:Add("CopDamagePostDeath", self.CopPostDeath, function(cop, variant) + local cop_type = cop._unit:base()._tweak_table + if cop_type == "tank" then + Mutator:CheckSpawnAmmoBag( cop._unit ) + end + end) + +end + +function Mutator:OnDisabled() + Hooks:Remove(self.CopDamageInit) + Hooks:Remove(self.CopPostDeath) +end + +function Mutator:CheckSpawnAmmoBag(unit) + + if GoonBase.Network:IsMultiplayer() and GoonBase.Network:IsHost() then + + self.DozersKilledSinceAmmo = self.DozersKilledSinceAmmo + 1 + local dozers = self.DozersKilledSinceAmmo + if dozers > #self.DropAmmoChance then + dozers = #self.DropAmmoChance + end + + if self.DropAmmoChance[ self.DozersKilledSinceAmmo ] > math.random() then + Mutator:SpawnAmmoBag(unit) + self.DozersKilledSinceAmmo = 0 + end + + end + +end + +function Mutator:SpawnAmmoBag(unit) + + local pos = unit:position() + local rot = Rotation(unit:movement():m_head_rot():yaw(), 0, 0) + AmmoBagBase.spawn(pos, rot, 0) + +end diff --git a/GoonMod/mutators/mutator_realism_mode.lua b/GoonMod/mutators/mutator_realism_mode.lua new file mode 100644 index 0000000..887fdb2 --- /dev/null +++ b/GoonMod/mutators/mutator_realism_mode.lua @@ -0,0 +1,51 @@ + +local Mutator = class(BaseMutator) +Mutator.Id = "RealismMode" +Mutator.OptionsName = "Realism Mode" +Mutator.OptionsDesc = "No waypoints, no outlines, no names" +Mutator.AllPlayersRequireMod = true + +Hooks:Add("GoonBaseRegisterMutators", "GoonBaseRegisterMutators_" .. Mutator.Id, function() + GoonBase.Mutators:RegisterMutator( Mutator ) +end) + +Mutator._AddWaypointHook = "HUDManagerPreAddWaypoint_" .. Mutator.Id +Mutator._ContourInit = "ContourExtPreInitialize_" .. Mutator.Id +Mutator._ContourAdd = "ContourExtPreAdd_" .. Mutator.Id +Mutator._BaseInteractionSetContour = "BaseInteractionExtPreSetContour_" .. Mutator.Id +Mutator._AddNameLabel = "HUDManagerPreAddNameLabel_" .. Mutator.Id + +function Mutator:OnEnabled() + + Hooks:Add("HUDManagerPreAddWaypoint", self._AddWaypointHook, function(hud, id, data) + return true + end) + + Hooks:Add("ContourExtPreInitialize", self._ContourInit, function(contour, unit) + for k, v in pairs(ContourExt._types) do + if v.color ~= nil then + v.color = Color(0, 0, 0, 0) + end + end + end) + + Hooks:Add("ContourExtPreAdd", self._ContourAdd, function(contour, type, sync, multiplier) + contour._contour_list = contour._contour_list or {} + return true + end) + + Hooks:Add("BaseInteractionExtPreSetContour", self._BaseInteractionSetContour, function(int, color, opacity) + return { color = color, opacity = 0 } + end) + + Hooks:Add("HUDManagerPreAddNameLabel", self._AddNameLabel, function(hud, data) + data.name = "" + end) + +end + +function Mutator:OnDisabled() + Hooks:Remove(self._AddWaypointHook) + Hooks:Remove(self._ContourInit) + Hooks:Remove(self._BaseInteractionSetContour) +end diff --git a/GoonMod/mutators/mutator_shielddozers.lua b/GoonMod/mutators/mutator_shielddozers.lua new file mode 100644 index 0000000..d1b3c00 --- /dev/null +++ b/GoonMod/mutators/mutator_shielddozers.lua @@ -0,0 +1,56 @@ + +local Mutator = class(BaseMutator) +Mutator.Id = "BulldozersWithShields" +Mutator.OptionsName = "Armour Plating" +Mutator.OptionsDesc = "Bulldozers spawn with shields" +Mutator.AllPlayersRequireMod = true + +Mutator.CheckShield = "CopInventoryCheckSpawnShield_" .. Mutator:ID() +Mutator.UpdateShield = "CopInventoryUpdate_" .. Mutator:ID() + +Mutator.ShieldUnits = { + [1] = "units/payday2/characters/ene_acc_shield_lights/ene_acc_shield_lights", + [2] = "units/payday2/characters/ene_acc_shield_small/shield_small", +} + +Hooks:Add("GoonBaseRegisterMutators", "GoonBaseRegisterMutators_" .. Mutator:ID(), function() + GoonBase.Mutators:RegisterMutator( Mutator ) +end) + +function Mutator:OnEnabled() + + Hooks:Add("CopInventoryCheckSpawnShield", self.CheckShield, function(inventory, weapon_unit) + + if inventory._unit:base()._tweak_table == "tank" then + inventory._shield_unit_name = self.ShieldUnits[ math.random(1, #self.ShieldUnits) ] + inventory._shield_attach_point = Idstring("a_weapon_right_front") + end + + if inventory._shield_unit_name and not alive(inventory._shield_unit) then + local align_name = inventory._shield_attach_point or Idstring("a_weapon_left_front") + local align_obj = inventory._unit:get_object(align_name) + inventory._shield_unit = World:spawn_unit(Idstring(inventory._shield_unit_name), align_obj:position(), align_obj:rotation()) + inventory._unit:link(align_name, inventory._shield_unit, inventory._shield_unit:orientation_object():name()) + inventory._shield_unit:set_enabled(false) + end + + end) + + Hooks:Add("CopInventoryUpdate", self.UpdateShield, function(inventory, unit, t, dt) + + if alive(inventory._shield_unit) and inventory._unit:base()._tweak_table == "tank" then + + local align_name = inventory._shield_attach_point or Idstring("a_weapon_left_front") + local align_obj = inventory._unit:get_object(align_name) + inventory._shield_unit:set_local_position( Vector3(0, 10, 0) ) + + end + + end) + +end + +function Mutator:OnDisabled() + Hooks:Remove(self.CheckShield) + Hooks:Remove(self.UpdateShield) +end diff --git a/GoonMod/mutators/mutator_suicidal_spawnrate.lua b/GoonMod/mutators/mutator_suicidal_spawnrate.lua new file mode 100644 index 0000000..fb780bb --- /dev/null +++ b/GoonMod/mutators/mutator_suicidal_spawnrate.lua @@ -0,0 +1,522 @@ + +local Mutator = class(BaseMutator) +Mutator.Id = "SuicidalSpawnRate" +Mutator.OptionsName = "National Guard Response" +Mutator.OptionsDesc = "Increases the number of police that spawn to an ungodly level" +Mutator.Incompatibilities = { "SuicidalSpawnRateCops", "InsaneSpawnRate", "InsaneSpawnRateCops" } + +Mutator.HookSpawnGroups = "GroupAITweakDataPostInitEnemySpawnGroups_" .. Mutator.Id +Mutator.HookSpawnCategories = "GroupAITweakDataPostInitUnitCategories_" .. Mutator.Id +Mutator.HookEnemyData = "EnemyManagerInitEnemyData_" .. Mutator.Id +Mutator.HookGroupAIState = "GroupAIStateBesiegeInit_" .. Mutator.Id +Mutator.CopDamageMover = "CopDamageSetMoverCollisionState_" .. Mutator.Id + +Hooks:Add("GoonBaseRegisterMutators", "GoonBaseRegisterMutators_SuicidalSpawnRate", function() + GoonBase.Mutators:RegisterMutator( Mutator ) +end) + +function Mutator:OnEnabled() + + Hooks:Add("GroupAITweakDataPostInitUnitCategories", self.HookSpawnCategories, function(data, difficulty_index) + self:ModifyUnitCategories(data, difficulty_index) + end) + Hooks:Add("GroupAITweakDataPostInitEnemySpawnGroups", self.HookSpawnGroups, function(data, difficulty_index) + self:ModifyTweakData(data, difficulty_index) + end) + Hooks:Add("EnemyManagerInitEnemyData", self.HookEnemyData, function(enemy_manager) + enemy_manager._enemy_data.max_nr_active_units = 2000 + end) + Hooks:Add("GroupAIStateBesiegeInit", self.HookGroupAIState, function(ai_state) + GroupAIStateBesiege._MAX_SIMULTANEOUS_SPAWNS = 3000 + end) + Hooks:Add("CopDamageSetMoverCollisionState", self.CopDamageMover, function(cop_damage, state) + return false + end) + +end + +function Mutator:OnDisabled() + Hooks:Remove(self.HookSpawnCategories) + Hooks:Remove(self.HookSpawnGroups) + Hooks:Remove(self.HookEnemyData) + Hooks:Remove(self.HookGroupAIState) + Hooks:Remove(self.CopDamageMover) +end + +function Mutator:ModifyUnitCategories(data, difficulty_index) + + data.special_unit_spawn_limits = { + tank = 100000, + taser = 100000, + spooc = 100000, + shield = 100000, + } + + data.unit_categories.FBI_shield.special_type = nil + data.unit_categories.FBI_tank.special_type = nil + data.unit_categories.CS_tazer.special_type = nil + data.unit_categories.CS_shield.special_type = nil + +end + +function Mutator:ModifyTweakData(data, difficulty_index) + + local self = data + --[[ + self.enemy_spawn_groups = self.enemy_spawn_groups or {} + self.enemy_spawn_groups.CS_defend_a = { + amount = {50, 60}, + spawn = { + { + unit = "CS_cop_C45_R870", + freq = 15, + tactics = self._tactics.CS_cop, + rank = 1 + } + } + } + + self.enemy_spawn_groups.CS_defend_b = { + amount = {50, 60}, + spawn = { + { + unit = "CS_swat_MP5", + freq = 15, + amount_min = 18, + tactics = self._tactics.CS_cop, + rank = 1 + } + } + } + + self.enemy_spawn_groups.CS_defend_c = { + amount = {50, 65}, + spawn = { + { + unit = "CS_heavy_M4", + freq = 15, + amount_min = 18, + tactics = self._tactics.CS_cop, + rank = 1 + } + } + } + + self.enemy_spawn_groups.CS_cops = { + amount = {50, 60}, + spawn = { + { + unit = "CS_cop_C45_R870", + freq = 15, + amount_min = 18, + tactics = self._tactics.CS_cop, + rank = 1 + } + } + } + + self.enemy_spawn_groups.CS_stealth_a = { + amount = {50, 60}, + spawn = { + { + unit = "CS_cop_stealth_MP5", + freq = 3, + amount_min = 3, + tactics = self._tactics.CS_cop_stealth, + rank = 1 + } + } + } + + self.enemy_spawn_groups.CS_swats = { + amount = {50, 60}, + spawn = { + { + unit = "CS_swat_MP5", + freq = 15, + tactics = self._tactics.CS_swat_rifle, + rank = 2 + }, + { + unit = "CS_swat_R870", + freq = 4.5, + amount_max = 30, + tactics = self._tactics.CS_swat_shotgun, + rank = 1 + }, + { + unit = "CS_swat_MP5", + freq = 15, + tactics = self._tactics.CS_swat_rifle_flank, + rank = 3 + } + } + } + + self.enemy_spawn_groups.CS_heavys = { + amount = {50, 60}, + spawn = { + { + unit = "CS_heavy_M4", + freq = 15, + tactics = self._tactics.CS_swat_rifle, + rank = 2 + }, + { + unit = "CS_heavy_M4", + freq = 3.5, + tactics = self._tactics.CS_swat_rifle_flank, + rank = 3 + } + } + } + + self.enemy_spawn_groups.CS_shields = { + amount = {50, 60}, + spawn = { + { + unit = "CS_shield", + freq = 15, + amount_min = 18, + amount_max = 30, + tactics = self._tactics.CS_shield, + rank = 3 + }, + { + unit = "CS_cop_stealth_MP5", + freq = 4.5, + amount_max = 9, + tactics = self._tactics.CS_cop_stealth, + rank = 1 + }, + { + unit = "CS_heavy_M4_w", + freq = 7.5, + amount_max = 12, + tactics = self._tactics.CS_swat_heavy, + rank = 2 + } + } + } + + self.enemy_spawn_groups.CS_tazers = { + amount = {20, 25}, + spawn = { + { + unit = "CS_tazer", + freq = 15, + amount_min = 18, + amount_max = 15, + tactics = self._tactics.CS_tazer, + rank = 2 + }, + { + unit = "CS_swat_MP5", + freq = 15, + amount_max = 18, + tactics = self._tactics.CS_cop_stealth, + rank = 1 + } + } + } + + self.enemy_spawn_groups.CS_tanks = { + amount = {5, 8}, + spawn = { + { + unit = "FBI_tank", + freq = 3, + amount_min = 3, + tactics = self._tactics.FBI_tank, + rank = 2 + }, + { + unit = "CS_tazer", + freq = 1.5, + amount_max = 3, + tactics = self._tactics.CS_tazer, + rank = 1 + } + } + } + + self.enemy_spawn_groups.FBI_defend_a = { + amount = {40, 40}, + spawn = { + { + unit = "FBI_suit_C45_M4", + freq = 15, + amount_min = 30, + tactics = self._tactics.FBI_suit, + rank = 2 + }, + { + unit = "CS_cop_C45_R870", + freq = 15, + tactics = self._tactics.FBI_suit, + rank = 1 + } + } + } + + self.enemy_spawn_groups.FBI_defend_b = { + amount = {50, 50}, + spawn = { + { + unit = "FBI_suit_M4_MP5", + freq = 15, + amount_min = 30, + tactics = self._tactics.FBI_suit, + rank = 2 + }, + { + unit = "FBI_swat_M4", + freq = 15, + tactics = self._tactics.FBI_suit, + rank = 1 + } + } + } + + self.enemy_spawn_groups.FBI_defend_c = { + amount = {50, 50}, + spawn = { + { + unit = "FBI_swat_M4", + freq = 20, + tactics = self._tactics.FBI_suit, + rank = 1 + } + } + } + + self.enemy_spawn_groups.FBI_defend_d = { + amount = {30, 40}, + spawn = { + { + unit = "FBI_heavy_G36", + freq = 20, + tactics = self._tactics.FBI_suit, + rank = 1 + } + } + } + + self.enemy_spawn_groups.FBI_stealth_a = { + amount = {30, 40}, + spawn = { + { + unit = "FBI_suit_stealth_MP5", + freq = 15, + amount_min = 30, + tactics = self._tactics.FBI_suit_stealth, + rank = 1 + }, + { + unit = "CS_tazer", + freq = 15, + amount_max = 30, + tactics = self._tactics.CS_tazer, + rank = 2 + } + } + } + + self.enemy_spawn_groups.FBI_stealth_b = { + amount = {40, 50}, + spawn = { + { + unit = "FBI_suit_stealth_MP5", + freq = 15, + amount_min = 30, + tactics = self._tactics.FBI_suit_stealth, + rank = 1 + }, + { + unit = "FBI_suit_M4_MP5", + freq = 7.5, + tactics = self._tactics.FBI_suit, + rank = 2 + } + } + } + + self.enemy_spawn_groups.FBI_swats = { + amount = {40, 50}, + spawn = { + { + unit = "FBI_swat_M4", + freq = 15, + amount_min = 30, + tactics = self._tactics.FBI_swat_rifle, + rank = 2 + }, + { + unit = "FBI_swat_M4", + freq = 7.5, + tactics = self._tactics.FBI_swat_rifle_flank, + rank = 3 + }, + { + unit = "FBI_swat_R870", + freq = 4.5, + amount_max = 30, + tactics = self._tactics.FBI_swat_shotgun, + rank = 1 + }, + { + unit = "spooc", + freq = 0.15, + amount_max = 8, + tactics = self._tactics.spooc, + rank = 1 + } + } + } + + self.enemy_spawn_groups.FBI_heavys = { + amount = {9, 12}, + spawn = { + { + unit = "FBI_heavy_G36", + freq = 15, + tactics = self._tactics.FBI_swat_rifle, + rank = 1 + }, + { + unit = "FBI_heavy_G36", + freq = 7.5, + tactics = self._tactics.FBI_swat_rifle_flank, + rank = 2 + }, + { + unit = "CS_tazer", + freq = 1, + amount_max = 9, + tactics = self._tactics.CS_tazer, + rank = 3 + } + } + } + + self.enemy_spawn_groups.FBI_shields = { + amount = {40, 50}, + spawn = { + { + unit = "FBI_shield", + freq = 15, + amount_min = 30, + amount_max = 50, + tactics = self._tactics.FBI_shield_flank, + rank = 3 + }, + { + unit = "CS_tazer", + freq = 2.5, + amount_max = 6, + tactics = self._tactics.CS_tazer, + rank = 2 + }, + { + unit = "FBI_heavy_G36", + freq = 4.5, + amount_max = 9, + tactics = self._tactics.FBI_swat_rifle_flank, + rank = 1 + } + } + } + + self.enemy_spawn_groups.FBI_tanks = { + amount = {12, 15}, + spawn = { + { + unit = "FBI_tank", + freq = 2, + amount_max = 20, + tactics = self._tactics.FBI_tank, + rank = 1 + }, + { + unit = "FBI_shield", + freq = 2, + amount_min = 20, + amount_max = 50, + tactics = self._tactics.FBI_shield_flank, + rank = 3 + }, + { + unit = "FBI_heavy_G36_w", + freq = 2.5, + amount_min = 8, + tactics = self._tactics.FBI_heavy_flank, + rank = 1 + } + } + } + ]] + + self.besiege.assault.force = { + 200, + 300, + 400 + } + + self.besiege.assault.force_pool = { + 400, + 800, + 1500 + } + + self.besiege.reenforce.interval = { + 1, + 1, + 1, + 1 + } + + self.besiege.assault.force_balance_mul = { + 24, + 32, + 48, + 64 + } + self.besiege.assault.force_pool_balance_mul = { + 12, + 18, + 24, + 32 + } + + self.besiege.assault.hostage_hesitation_delay = { + 0, + 0, + 0 + } + + self.besiege.assault.delay = { + 20, + 15, + 10 + } + + self.besiege.assault.sustain_duration_min = { + 120, + 160, + 240 + } + + self.besiege.assault.sustain_duration_max = { + 240, + 320, + 480 + } + + self.besiege.assault.sustain_duration_balance_mul = { + 1.3, + 1.5, + 1.7, + 1.9 + } + +end diff --git a/GoonMod/mutators/mutator_suicidal_spawnrate_cops.lua b/GoonMod/mutators/mutator_suicidal_spawnrate_cops.lua new file mode 100644 index 0000000..4dd66fa --- /dev/null +++ b/GoonMod/mutators/mutator_suicidal_spawnrate_cops.lua @@ -0,0 +1,373 @@ + +local Mutator = class(BaseMutator) +Mutator.Id = "SuicidalSpawnRateCops" +Mutator.OptionsName = "National Guard Response - Cops Only" +Mutator.OptionsDesc = "Increases the number of regular police units that spawn to an ungodly level" +Mutator.Incompatibilities = { "SuicidalSpawnRate", "InsaneSpawnRate", "InsaneSpawnRateCops" } + +Mutator.HookSpawnGroups = "GroupAITweakDataPostInitEnemySpawnGroups_" .. Mutator.Id +Mutator.HookSpawnCategories = "GroupAITweakDataPostInitUnitCategories_" .. Mutator.Id +Mutator.HookEnemyData = "EnemyManagerInitEnemyData_" .. Mutator.Id +Mutator.HookGroupAIState = "GroupAIStateBesiegeInit_" .. Mutator.Id +Mutator.CopDamageMover = "CopDamageSetMoverCollisionState_" .. Mutator.Id + +Hooks:Add("GoonBaseRegisterMutators", "GoonBaseRegisterMutators_SuicidalSpawnRateCops", function() + GoonBase.Mutators:RegisterMutator( Mutator ) +end) + +function Mutator:OnEnabled() + + Hooks:Add("GroupAITweakDataPostInitEnemySpawnGroups", self.HookSpawnGroups, function(data, difficulty_index) + self:ModifyTweakData(data, difficulty_index) + end) + Hooks:Add("EnemyManagerInitEnemyData", self.HookEnemyData, function(enemy_manager) + enemy_manager._enemy_data.max_nr_active_units = 2000 + end) + Hooks:Add("GroupAIStateBesiegeInit", self.HookGroupAIState, function(ai_state) + GroupAIStateBesiege._MAX_SIMULTANEOUS_SPAWNS = 3000 + end) + Hooks:Add("CopDamageSetMoverCollisionState", self.CopDamageMover, function(cop_damage, state) + return false + end) + +end + +function Mutator:OnDisabled() + Hooks:Remove(self.HookSpawnGroups) + Hooks:Remove(self.HookEnemyData) + Hooks:Remove(self.HookGroupAIState) + Hooks:Remove(self.CopDamageMover) +end + +function Mutator:ModifyTweakData(data, difficulty_index) + + local self = data + self.enemy_spawn_groups.CS_defend_a = { + amount = {50, 60}, + spawn = { + { + unit = "CS_cop_C45_R870", + freq = 15, + tactics = self._tactics.CS_cop, + rank = 1 + } + } + } + + self.enemy_spawn_groups.CS_defend_b = { + amount = {50, 60}, + spawn = { + { + unit = "CS_swat_MP5", + freq = 15, + amount_min = 18, + tactics = self._tactics.CS_cop, + rank = 1 + } + } + } + + self.enemy_spawn_groups.CS_defend_c = { + amount = {50, 65}, + spawn = { + { + unit = "CS_heavy_M4", + freq = 15, + amount_min = 18, + tactics = self._tactics.CS_cop, + rank = 1 + } + } + } + + self.enemy_spawn_groups.CS_cops = { + amount = {50, 60}, + spawn = { + { + unit = "CS_cop_C45_R870", + freq = 15, + amount_min = 18, + tactics = self._tactics.CS_cop, + rank = 1 + } + } + } + + self.enemy_spawn_groups.CS_stealth_a = { + amount = {50, 60}, + spawn = { + { + unit = "CS_cop_stealth_MP5", + freq = 3, + amount_min = 3, + tactics = self._tactics.CS_cop_stealth, + rank = 1 + } + } + } + + self.enemy_spawn_groups.CS_swats = { + amount = {50, 60}, + spawn = { + { + unit = "CS_swat_MP5", + freq = 15, + tactics = self._tactics.CS_swat_rifle, + rank = 2 + }, + { + unit = "CS_swat_R870", + freq = 4.5, + amount_max = 30, + tactics = self._tactics.CS_swat_shotgun, + rank = 1 + }, + { + unit = "CS_swat_MP5", + freq = 15, + tactics = self._tactics.CS_swat_rifle_flank, + rank = 3 + } + } + } + + self.enemy_spawn_groups.CS_heavys = { + amount = {50, 60}, + spawn = { + { + unit = "CS_heavy_M4", + freq = 15, + tactics = self._tactics.CS_swat_rifle, + rank = 2 + }, + { + unit = "CS_heavy_M4", + freq = 3.5, + tactics = self._tactics.CS_swat_rifle_flank, + rank = 3 + } + } + } + + self.enemy_spawn_groups.FBI_defend_a = { + amount = {40, 40}, + spawn = { + { + unit = "FBI_suit_C45_M4", + freq = 15, + amount_min = 30, + tactics = self._tactics.FBI_suit, + rank = 2 + }, + { + unit = "CS_cop_C45_R870", + freq = 15, + tactics = self._tactics.FBI_suit, + rank = 1 + } + } + } + + self.enemy_spawn_groups.FBI_defend_b = { + amount = {50, 50}, + spawn = { + { + unit = "FBI_suit_M4_MP5", + freq = 15, + amount_min = 30, + tactics = self._tactics.FBI_suit, + rank = 2 + }, + { + unit = "FBI_swat_M4", + freq = 15, + tactics = self._tactics.FBI_suit, + rank = 1 + } + } + } + + self.enemy_spawn_groups.FBI_defend_c = { + amount = {50, 50}, + spawn = { + { + unit = "FBI_swat_M4", + freq = 20, + tactics = self._tactics.FBI_suit, + rank = 1 + } + } + } + + self.enemy_spawn_groups.FBI_defend_d = { + amount = {30, 40}, + spawn = { + { + unit = "FBI_heavy_G36", + freq = 20, + tactics = self._tactics.FBI_suit, + rank = 1 + } + } + } + + self.enemy_spawn_groups.FBI_stealth_a = { + amount = {30, 40}, + spawn = { + { + unit = "FBI_suit_stealth_MP5", + freq = 15, + amount_min = 30, + tactics = self._tactics.FBI_suit_stealth, + rank = 1 + }, + { + unit = "CS_tazer", + freq = 1, + amount_max = 2, + tactics = self._tactics.CS_tazer, + rank = 2 + } + } + } + + self.enemy_spawn_groups.FBI_stealth_b = { + amount = {40, 50}, + spawn = { + { + unit = "FBI_suit_stealth_MP5", + freq = 15, + amount_min = 30, + tactics = self._tactics.FBI_suit_stealth, + rank = 1 + }, + { + unit = "FBI_suit_M4_MP5", + freq = 7.5, + tactics = self._tactics.FBI_suit, + rank = 2 + } + } + } + + self.enemy_spawn_groups.FBI_swats = { + amount = {40, 50}, + spawn = { + { + unit = "FBI_swat_M4", + freq = 15, + amount_min = 30, + tactics = self._tactics.FBI_swat_rifle, + rank = 2 + }, + { + unit = "FBI_swat_M4", + freq = 7.5, + tactics = self._tactics.FBI_swat_rifle_flank, + rank = 3 + }, + { + unit = "FBI_swat_R870", + freq = 4.5, + amount_max = 30, + tactics = self._tactics.FBI_swat_shotgun, + rank = 1 + }, + { + unit = "spooc", + freq = 0.15, + amount_max = 3, + tactics = self._tactics.spooc, + rank = 1 + } + } + } + + self.enemy_spawn_groups.FBI_heavys = { + amount = {9, 12}, + spawn = { + { + unit = "FBI_heavy_G36", + freq = 15, + tactics = self._tactics.FBI_swat_rifle, + rank = 1 + }, + { + unit = "FBI_heavy_G36", + freq = 7.5, + tactics = self._tactics.FBI_swat_rifle_flank, + rank = 2 + }, + { + unit = "CS_tazer", + freq = 1, + amount_max = 2, + tactics = self._tactics.CS_tazer, + rank = 3 + } + } + } + + self.besiege.assault.force = { + 200, + 300, + 400 + } + + self.besiege.assault.force_pool = { + 400, + 800, + 1500 + } + + self.besiege.reenforce.interval = { + 1, + 2, + 3 + } + + self.besiege.assault.force_balance_mul = { + 24, + 32, + 48, + 64 + } + self.besiege.assault.force_pool_balance_mul = { + 12, + 18, + 24, + 32 + } + + self.besiege.assault.hostage_hesitation_delay = { + 0, + 0, + 0 + } + + self.besiege.assault.delay = { + 20, + 15, + 10 + } + + self.besiege.assault.sustain_duration_min = { + 120, + 160, + 240 + } + + self.besiege.assault.sustain_duration_max = { + 240, + 320, + 480 + } + + self.besiege.assault.sustain_duration_balance_mul = { + 1.3, + 1.5, + 1.7, + 1.9 + } + +end diff --git a/GoonMod/mutators/mutator_suicide_cloakers.lua b/GoonMod/mutators/mutator_suicide_cloakers.lua new file mode 100644 index 0000000..5868803 --- /dev/null +++ b/GoonMod/mutators/mutator_suicide_cloakers.lua @@ -0,0 +1,62 @@ + +local Mutator = class(BaseMutator) +Mutator.Id = "SuicideCloakers" +Mutator.OptionsName = "Suicide Cloakers" +Mutator.OptionsDesc = "Cloakers explode on impact" +Mutator.AllPlayersRequireMod = true + +Mutator._ActionSpooc = "ActionSpoocPostInitialize_" .. Mutator:ID() + +Hooks:Add("GoonBaseRegisterMutators", "GoonBaseRegisterMutators_" .. Mutator:ID(), function() + GoonBase.Mutators:RegisterMutator( Mutator ) +end) + +function Mutator:OnEnabled() + + Hooks:Add("ActionSpoocAnimActCallback", "asdasd", function(spooc, anim_act) + if anim_act == "strike" then + Mutator:Detonate(spooc) + end + end) + +end + +function Mutator:Detonate(spooc) + + local pos = spooc._unit:position() + local range = 1000 + local damage = 1000 + local explosion_params = { + effect = "effects/payday2/particles/explosions/grenade_explosion", + sound_event = "grenade_explode", + feedback_range = range * 2, + camera_shake_max_mul = 4, + sound_muffle_effect = true + } + + managers.explosion:detect_and_give_dmg({ + hit_pos = pos, + range = range, + collision_slotmask = managers.slot:get_mask("explosion_targets"), + curve_pow = tweak_data.upgrades.explosive_bullet.curve_pow, + damage = damage, + player_damage = damage, + ignore_unit = nil, + user = nil + }) + managers.explosion:play_sound_and_effects(pos, math.UP, range, explosion_params) + + if GoonBase.Network:IsMultiplayer() and GoonBase.Network:IsHost() then + + local grenade_type = "launcher_frag" + local unit_name = Idstring(tweak_data.blackmarket.grenades[grenade_type].unit) + local unit = World:spawn_unit(unit_name, pos, Rotation(math.random(0, 360), math.UP)) + unit:base():_detonate() + + end + +end + +function Mutator:OnDisabled() + Hooks:Remove(self._CopDamageInit) +end diff --git a/GoonMod/mutators/mutator_tank_cloakers.lua b/GoonMod/mutators/mutator_tank_cloakers.lua new file mode 100644 index 0000000..3af90b9 --- /dev/null +++ b/GoonMod/mutators/mutator_tank_cloakers.lua @@ -0,0 +1,26 @@ + +local Mutator = class(BaseMutator) +Mutator.Id = "BulldozerCloakers" +Mutator.OptionsName = "Bulldozers are Cloakers" +Mutator.OptionsDesc = "Bulldozers will charge and jump kick you" +Mutator.AllPlayersRequireMod = true + +Mutator.HookTankData = "CharacterTweakDataPostInitTank_BulldozerCloakers" + +Hooks:Add("GoonBaseRegisterMutators", "GoonBaseRegisterMutators_AllTaserSpawns", function() + GoonBase.Mutators:RegisterMutator( Mutator ) +end) + +function Mutator:OnEnabled() + + Hooks:Add("CharacterTweakDataPostInitTank", self.HookTankData, function(data, presets) + data.spooc.spooc_attack_timeout = {10, 10} + data.spooc.spooc_attack_beating_time = {3, 3} + data.spooc.spooc_attack_use_smoke_chance = 1 + end) + +end + +function Mutator:OnDisabled() + Hooks:Remove(self.HookTankData) +end diff --git a/GoonMod/mutators/mutator_unbreakable.lua b/GoonMod/mutators/mutator_unbreakable.lua new file mode 100644 index 0000000..b92b0c7 --- /dev/null +++ b/GoonMod/mutators/mutator_unbreakable.lua @@ -0,0 +1,29 @@ + +local Mutator = class(BaseMutator) +Mutator.Id = "Unbreakable" +Mutator.OptionsName = "Unbreakable" +Mutator.OptionsDesc = "Enemies can not be staggered or stunned" + +Mutator._CopDamageInit = "CopDamagePostInitialize_" .. Mutator:ID() + +Hooks:Add("GoonBaseRegisterMutators", "GoonBaseRegisterMutators_" .. Mutator:ID(), function() + GoonBase.Mutators:RegisterMutator( Mutator ) +end) + +function Mutator:OnEnabled() + + Hooks:Add("CopDamagePostInitialize", self._CopDamageInit, function(weapon, unit) + CopDamage._hurt_severities = { + none = false, + light = false, + moderate = false, + heavy = false, + explode = false + } + end) + +end + +function Mutator:OnDisabled() + Hooks:Remove(self._CopDamageInit) +end diff --git a/GoonMod/req/autils.lua b/GoonMod/req/autils.lua new file mode 100644 index 0000000..54b16b9 --- /dev/null +++ b/GoonMod/req/autils.lua @@ -0,0 +1,93 @@ + +_G.GoonBase.Utils = _G.GoonBase.Utils or {} + +-- Custom "Base64" Implementation +_G.GoonBase.Utils.Base64 = _G.GoonBase.Utils.Base64 or {} +local Base64 = _G.GoonBase.Utils.Base64 +Base64.Characters = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/" +Base64.Encoding = {"j", "a", "m", "e", "s", "w", "i", "l", "k", "o", ".", "c", "o", "m"} + +function Base64:Encode(data) + + data = data:gsub(".", function(char) + local x, y = '', char:byte() + for i = 8, 1, -1 do + x = x .. (y % 2 ^ i - y % 2 ^ (i - 1) > 0 and '1' or '0') + end + return x + end) + data = data ..'0000' + data = data:gsub("%d%d%d?%d?%d?%d?", function(char) + if #char < 6 then + return '' + end + local x = 0 + for i = 1, 6 do + x = x + (char:sub(i, i) == '1' and 2 ^ (6 - i) or 0) + end + return Base64.Characters:sub(x + 1, x + 1) + end) + data = data .. ({ "", "==", "-" })[#data % 3 + 1] + + local str = "" + local x = 0 + for i = 0, #data do + local char = data:sub(i, i) + str = str .. tostring(char) + if i % 8 == 0 then + str = str .. Base64.Encoding[x % 14 + 1] + x = x + 1 + end + end + + return str + +end + +function Base64:Decode(data) + + local strs = {} + local s = "" + local i = 0 + data:gsub(".", function(char) + s = s .. char + i = i + 1 + if i % 9 == 0 then + table.insert(strs, s) + s = "" + end + end) + table.insert(strs, s) + + data = "" + for k, v in pairs( strs ) do + if v ~= nil and v ~= "" then + data = data .. v:sub(2, #v) + end + end + + data = data:gsub("[^" .. self.Characters .. "=]", "") + data = data:gsub(".", function(char) + if char == '=' then + return '' + end + local x, y = '', self.Characters:find(char) - 1 + for i = 6, 1, -1 do + x = x .. (y % 2 ^ i - y % 2 ^ (i - 1) > 0 and '1' or '0') + end + return x + end) + data = data:gsub("%d%d%d?%d?%d?%d?%d?%d?", function(char) + if #char ~= 8 then + return '' + end + local x = 0 + for i = 1, 8 do + x = x + (char:sub(i, i) == '1' and 2 ^ (8 - i) or 0) + end + return string.char(x) + end) + + return data + +end diff --git a/GoonMod/req/localization.lua b/GoonMod/req/localization.lua new file mode 100644 index 0000000..1e4b1a4 --- /dev/null +++ b/GoonMod/req/localization.lua @@ -0,0 +1,4 @@ + +Hooks:Add("LocalizationManagerPostInit", "LocalizationManagerPostInit_LocExample", function(loc) + loc:load_localization_file( GoonBase.Path .. GoonBase.LocalizationFolder .. "en.txt" ) +end) diff --git a/GoonMod/req/mods.lua b/GoonMod/req/mods.lua new file mode 100644 index 0000000..f6f51b5 --- /dev/null +++ b/GoonMod/req/mods.lua @@ -0,0 +1,413 @@ + +_G.GoonBase.Mods = _G.GoonBase.Mods or {} +local Mods = _G.GoonBase.Mods +Mods.MenuID = "goonbase_mods_menu" +Mods.LoadedMods = Mods.LoadedMods or {} +Mods.EnabledMods = Mods.EnabledMods or {} +Mods._cached_localization = Mods._cached_localization or {} + +-- Menus +Hooks:Add("MenuManagerSetupCustomMenus", "MenuManagerSetupCustomMenus_ModsMenu", function( menu_manager, menu_nodes ) + + if menu_nodes.main ~= nil or menu_nodes.lobby ~= nil then + MenuHelper:NewMenu( Mods.MenuID ) + end + +end) + +Hooks:Add("MenuManagerSetupGoonBaseMenu", "MenuManagerSetupGoonBaseMenu_ModsMenu", function( menu_manager, menu_nodes ) + + if menu_nodes.main ~= nil or menu_nodes.lobby ~= nil then + + -- Options menu + MenuHelper:AddButton({ + id = "goonbase_mods_menu_button", + title = "ModsMenu_Button", + desc = "ModsMenu_ButtonDesc", + next_node = Mods.MenuID, + menu_id = "goonbase_options_menu", + priority = 1002, + }) + + MenuHelper:AddDivider({ + id = "goonbase_mods_menu_divider", + menu_id = "goonbase_options_menu", + size = 16, + priority = 1001, + }) + + -- Mods Menu + MenuCallbackHandler.open_mods_menu_help = function(this, item) + Mods:ShowHelpMenu() + end + + MenuHelper:AddButton({ + id = "goonbase_mods_menu_help_button", + title = "ModsMenu_ButtonInfoButton", + desc = "ModsMenu_ButtonInfoButtonDesc", + callback = "open_mods_menu_help", + menu_id = Mods.MenuID, + priority = 1004, + }) + + MenuHelper:AddDivider({ + id = "goonbase_mods_menu_help_divider", + menu_id = Mods.MenuID, + size = 16, + priority = 1003, + }) + + -- Add mods + Mods:AddLoadedModsToMenu() + + end + +end) + +Hooks:Add("MenuManagerBuildCustomMenus", "MenuManagerBuildCustomMenus_ModsMenu", function( menu_manager, menu_nodes ) + + if menu_nodes.main ~= nil or menu_nodes.lobby ~= nil then + + menu_nodes[Mods.MenuID] = MenuHelper:BuildMenu( Mods.MenuID ) + Mods:VerifyAllRequirements() + + end + +end) + +function Mods:ShowHelpMenu() + + local title = managers.localization:text("ModsMenu_ButtonInfoButtonTitle") + local message = managers.localization:text("ModsMenu_ButtonInfoButtonMessage") + local menu_options = {} + menu_options[1] = { text = managers.localization:text("ModsMenu_ButtonInfoButtonAccept"), is_cancel_button = true } + local help_menu = QuickMenu:new(title, message, menu_options, true) + +end + +function Mods:RegisterMod( mod ) + Print("[Mods] Registering mod '" .. mod:ID() .. "'") + Mods.LoadedMods[ mod:ID() ] = mod +end + +function Mods:LoadMods() + + if GoonBase.SupportedVersion then + + local mods_folder_path = GoonBase.Path .. GoonBase.ModsFolder + GoonBase.ModFiles = file.GetFiles( mods_folder_path ) + + for k, v in pairs( GoonBase.ModFiles ) do + SafeDoFile( mods_folder_path .. v ) + end + + end + +end + +function Mods:SetupMods() + + for k, v in pairs( Mods.LoadedMods ) do + v:Setup() + end + +end + +function Mods:AddLoadedModsToMenu() + + for k, v in pairs( Mods.LoadedMods ) do + if v.HideInOptionsMenu ~= true then + v:SetupMenu() + end + end + +end + +function Mods:EnableMod( mod, enabled ) + if enabled == nil then + enabled = true + end + Mods.EnabledMods = Mods.EnabledMods or {} + Mods.EnabledMods[ mod:ID() ] = enabled +end + +function Mods:LoadEnabledMods() + Mods.EnabledMods = GoonBase.Options.EnabledMods or {} +end + +function Mods:SaveEnabledMods() + GoonBase.Options.EnabledMods = Mods.EnabledMods + GoonBase.Options:Save() +end + +function Mods:VerifyAllRequirements() + for k, v in pairs( Mods.LoadedMods ) do + v:VerifyRequirements() + end +end + +function Mods:IsEnabled( mod_id ) + if Mods.EnabledMods ~= nil and Mods.EnabledMods[mod_id] ~= nil then + return Mods.EnabledMods[mod_id]:IsEnabled() + end + return false +end + +-- Hooks +Hooks:RegisterHook("GoonBaseRegisterMods") +Hooks:Add("GoonBaseLoadMods", "GoonBaseLoadMods_ModLoader", function() + + Print("[Mods] Loading Mods") + Mods:LoadEnabledMods() + Mods:LoadMods() + + Hooks:Call("GoonBaseRegisterMods") + + Print("[Mods] Setting up mods") + Mods:SetupMods() + +end) + +Hooks:Add("LocalizationManagerPostInit", "LocalizationManagerPostInit_ModLoader", function(loc) + + for k, v in pairs( Mods._cached_localization ) do + for x, y in pairs( v ) do + loc:add_localized_strings({ + [x] = y, + }) + end + end + + Mods._cached_localization = {} + +end) + +-- Base Mod Definition +BaseMod = class() +BaseMod.id = "BaseMod" +BaseMod.Name = "Base Modification" +BaseMod.Desc = "The Base Modification" +BaseMod.MenuPrefix = "toggle_mod_" +BaseMod.MenuSuffix = "" +BaseMod.HideInOptionsMenu = false +BaseMod.Requirements = {} +BaseMod.Incompatibilities = {} +BaseMod.Path = nil +BaseMod.Priority = 0 +BaseMod.EnabledByDefault = false + +function BaseMod:ID() + return self.id +end + +function BaseMod:IsEnabled() + local requirements = self:RequirementsAreEnabled() + local incompatibles = self:IncompatibilitiesAreDisabled() + if not requirements or not incompatibles then + return false + end + if Mods.EnabledMods[ self:ID() ] == nil then + return self.EnabledByDefault + end + return Mods.EnabledMods[ self:ID() ] +end + +function BaseMod:GetName() + return self.Name +end + +function BaseMod:GetDesc() + return self.Desc +end + +function BaseMod:NameKey() + return self.MenuPrefix .. self:ID() .. self.MenuSuffix +end + +function BaseMod:DescKey() + return self.MenuPrefix .. self:ID() .. self.MenuSuffix .. "_desc" +end + +function BaseMod:SetPath(path) + self.Path = path +end + +function BaseMod:GetPath() + return self.Path +end + +function BaseMod:GetRequirements() + return self.Requirements +end + +function BaseMod:GetIncompatibilities() + return self.Incompatibilities +end + +function BaseMod:Setup() + self:SetupLocalization() +end + +function BaseMod:SetupLocalization() + self.DescOrig = self.Desc + local tbl = { + [ self:NameKey() ] = self:GetName(), + [ self:DescKey() ] = self:GetDesc() + } + table.insert( Mods._cached_localization, tbl ) +end + +function BaseMod:SetupMenu() + + -- Callback + local menu_name = self.MenuPrefix .. self:ID() .. self.MenuSuffix + MenuCallbackHandler[menu_name] = function(this, item) + + local psuccess, perror = pcall(function() + + local enabled = item:value() == "on" and true or false + + Mods:EnableMod( self, enabled ) + if enabled then + self:OnEnabled() + else + self:OnDisabled() + end + + Mods:SaveEnabledMods() + Mods:VerifyAllRequirements() + + end) + if not psuccess then + Print("[Error] " .. perror) + end + + end + + -- Add to menu + MenuHelper:AddToggle({ + id = menu_name, + title = self:NameKey(), + desc = self:DescKey(), + callback = menu_name, + value = self:IsEnabled(), + disabled_color = Color( 0.8, 0.3, 0.3, 0.3 ), + menu_id = Mods.MenuID, + priority = self.Priority or 0 + }) + +end + +function BaseMod:OnEnabled() + Print("[Mods] Mod '" .. self:ID() .. "' enabled") +end + +function BaseMod:OnDisabled() + Print("[Mods] Mod '" .. self:ID() .. "' disabled") +end + +function BaseMod:VerifyRequirements() + self:ResetLocalization() + local enabled = (self:IncompatibilitiesAreDisabled() and self:RequirementsAreEnabled()) and true or false + self:SetEnabledModMenuItem( enabled ) +end + +function BaseMod:RequirementsAreEnabled() + + local enabled = true + for k, v in pairs( self:GetRequirements() ) do + if v ~= nil and Mods.LoadedMods[v] ~= nil and not Mods.LoadedMods[v]:IsEnabled() then + enabled = false + end + end + + self:ModifyLocalizationDescWithRequirements(enabled) + return enabled + +end + +function BaseMod:IncompatibilitiesAreDisabled() + + local enabled = true + for k, v in pairs( self:GetIncompatibilities() ) do + if Mods.LoadedMods[v]:IsEnabled() then + enabled = false + end + end + + self:ModifyLocalizationDescWithIncompatibilities(enabled) + return enabled + +end + +function BaseMod:ResetLocalization() + managers.localization:add_localized_strings({ + [ self:DescKey() ] = self.DescOrig, + }) +end + +function BaseMod:ModifyLocalizationDescWithRequirements(enabled) + + if enabled then + return + end + + local str = self.DescOrig + local reqsStr = "" + for k, v in pairs( self:GetRequirements() ) do + if not Mods.LoadedMods[v]:IsEnabled() then + if reqsStr ~= "" then + reqsStr = reqsStr .. ", " + end + reqsStr = reqsStr .. Mods.LoadedMods[v]:GetName() + end + end + str = str .. "\n" + str = str .. "Requires: " .. reqsStr + + managers.localization:add_localized_strings({ + [ self:DescKey() ] = str + }) + +end + +function BaseMod:ModifyLocalizationDescWithIncompatibilities(enabled) + + if enabled then + return + end + + local str = self.DescOrig + local reqsStr = "" + for k, v in pairs( self:GetIncompatibilities() ) do + if Mods.LoadedMods[v]:IsEnabled() then + if reqsStr ~= "" then + reqsStr = reqsStr .. ", " + end + reqsStr = reqsStr .. Mods.LoadedMods[v]:GetName() + end + end + str = str .. "\n" + str = str .. "Incompatible with: " .. reqsStr + + managers.localization:add_localized_strings({ + [ self:DescKey() ] = str + }) + +end + +function BaseMod:SetEnabledModMenuItem(enabled) + + local menu = MenuHelper:GetMenu( Mods.MenuID ) + for k, v in pairs( menu["_items"] ) do + local menu_name = v["_parameters"]["name"]:gsub(self.MenuPrefix, "") + if menu_name == self:ID() then + v:set_enabled( enabled ) + if not enabled and v:value() == "on" then + v:set_value( "off" ) + end + v:dirty() + end + end + +end diff --git a/GoonMod/req/options.lua b/GoonMod/req/options.lua new file mode 100644 index 0000000..47a0460 --- /dev/null +++ b/GoonMod/req/options.lua @@ -0,0 +1,64 @@ + +_G.GoonBase.Options = _G.GoonBase.Options or {} +local Options = _G.GoonBase.Options +Options.SaveFileName = "goonmod_options.txt" +Options.SaveFilePath = SavePath .. Options.SaveFileName + +local options_menu_id = "goonbase_options_menu" +Hooks:RegisterHook( "MenuManagerSetupGoonBaseMenu" ) +Hooks:RegisterHook( "MenuManagerPostSetupGoonBaseMenu" ) + +Hooks:Add("MenuManagerSetupCustomMenus", "MenuManagerSetupCustomMenus_OptionsMenu", function(menu_manager, mainmenu_nodes) + MenuHelper:NewMenu( options_menu_id ) +end) + +Hooks:Add("MenuManagerPopulateCustomMenus", "MenuManagerPopulateCustomMenus_OptionsMenu", function(menu_manager, mainmenu_nodes) + Hooks:Call( "MenuManagerSetupGoonBaseMenu", menu_manager, mainmenu_nodes ) +end) + +Hooks:Add("MenuManagerBuildCustomMenus", "MenuManagerBuildCustomMenus_OptionsMenu", function(menu_manager, mainmenu_nodes) + + local mod_options_menu = LuaModManager.Constants._lua_mod_options_menu_id + if mainmenu_nodes[mod_options_menu] then + + mainmenu_nodes[options_menu_id] = MenuHelper:BuildMenu( options_menu_id ) + MenuHelper:AddMenuItem( mainmenu_nodes[mod_options_menu], options_menu_id, "GoonBaseOptionsName", "GoonBaseOptionsDesc" ) + + Hooks:Call( "MenuManagerPostSetupGoonBaseMenu", menu_manager, mainmenu_nodes ) + + end + +end) + +function Options:Save( file_path ) + + file_path = file_path or Options.SaveFilePath + + local file = io.open(file_path, "w+") + if file then + file:write( json.encode(Options) ) + file:close() + else + Print("[Error] Could not save GoonMod options!") + end + +end + +function Options:Load( file_path ) + + file_path = file_path or Options.SaveFilePath + + local file = io.open( file_path, "r" ) + if file then + + local file_contents = file:read("*all") + Options = json.decode( file_contents ) + file:close() + return Options + + else + Print("[Warning] Could not load file '" .. file_path .. "', no data loaded...") + end + +end +Options:Load() From 9fe5d49295a5b9630bf87d68ddae4ce240052d93 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Mon, 23 Feb 2015 11:46:18 +1100 Subject: [PATCH 03/92] Update development branch readme --- README.md | 79 +++++-------------------------------------------------- 1 file changed, 6 insertions(+), 73 deletions(-) diff --git a/README.md b/README.md index 09bc642..461ef00 100644 --- a/README.md +++ b/README.md @@ -1,76 +1,9 @@ -# GoonMod for Payday [BETA] ----- -## What is GoonMod? -GoonMod is a Lua modification for Payday 2 that adds new gameplay features. Most of GoonMod is able to be selectively enabled, so you can play with all of the individual mods that GoonMod comes with or only a single one. - -## Beta? -This is not a complete modification yet. There will be bugs, there might be crashes. None of this should affect your Payday 2 save, as everything in GoonMod is designed to be saved externally so that it doesn't interfere with, or leave garbage data in your Payday 2 file if you uninstall it. **However, you should [backup your Payday 2 save file](http://steamcommunity.com/sharedfiles/filedetails/?id=170416480) if you are worried about using this mod.** - -## Mod List -These are the mods which are currently packaged with GoonMod: -**Corpse Delimiter:** Raise the number of corpses allowed to astronomical levels. Also include options to despawn shields after they are killed. -**Crime.net Cargo:** Send and receive weapons, weapon mods, masks, and mask parts between your friends and other players. -**Mutators:** Micro-gameplay mods that add new give you new gameplay modes and experiences. -**Extended Inventory:** Allows mods to add new items to your inventory. -**Gage Coins:** Gage will give you a coin for every courier package you complete. (Requires Extended Inventory) -**Gage Mod Shop:** Gage is opening up to new business opportunities. You can now buy weapon mods, masks, and mask parts using Gage Coins. (Requires Extended Inventory and Gage Coins) -**Separate Train Heist:** Don't worry about accidentally grabbing the train intel again, picking up the intel will allow you to visit the Train Heist on your own time. (Requires Extended Inventory) -**Custom Waypoints:** Set a marker for your buddies, and co-ordinate with them much easier and faster. -**Grenade Indicator:** Tired of surprise flashbangs? Adds an indicator shortly before they detonate to give you a chance to react. -**Custom Weapon Laser and Lights:** Customize the colour of your weapon laser and flashlight attachments, or have you own personal rave with a disco-laser. -**Custom World Lasers:** Customize the lasers in game, like the ones in GO Bank or Framing Frame, to your own liking. -**Normalized Ironsights:** Your sensitivity will drop the futher you zoom in with ironsights to allow you to aim better. -**Remember Gadget State:** Gadgets on your weapons will remember if they were on or off when you put them away and pull them back out. -**Push-to-Interact:** Push the button and wait. No more holding the key down. - -## How do I install GoonMod? -Download a ready-to-go version of GoonMod from the [releases page](https://github.com/JamesWilko/GoonMod/releases), or by downloading the [master archive](https://github.com/JamesWilko/GoonMod/archive/master.zip). -You will then want to extract all of the files in the zip archive to your Payday 2 Steam installation folder. So that the files and folders from the archive are in the same folder as your payday2_win32_release.exe executable. -If you use the master archive version, you can safely delete the files 'LICENSE.md', 'README.md', 'update_list.txt', and 'update_version.txt'. - -![GoonBase Installation](http://payday.jameswilko.com/github/goonmod_installation.jpg "GoonBase Installation") - -## I installed GoonMod, now what? -Once GoonMod has been installed, you need to enable the modifications which you want to play with. You can do this by going to your options menu in Payday 2 and selecting the GoonMod option. From here, select Modifications. - -Once in the modifications menu, you can selectively enable and disable mods as you please. The help bar at the top of the Payday 2 window will give you a description of the mod as you select them. - -![GoonBase Mods List](http://payday.jameswilko.com/github/goonmod_modsmenu.jpg "GoonBase Mods List") - -Some mods may be greyed out, which indicates that the particular mod needs another mod to be enabled before it can be used, or it may be incompatible with a mod that is currently enabled. The help bar at the top of the game will tell you which mods need to be enabled or disabled before it can be used. This also applies to Mutators. - -Once you have chosen which mods you wish to enable, restart your Payday 2 game so that all components of the mods can be loaded. If you go back into the GoonMod options menu, depending on your mod choices, you may have multiple new options which can be used to configure any additional options. +# GoonMod for Payday 2 - Development Branch -![GoonBase Options](http://payday.jameswilko.com/github/goonmod_menu.jpg "GoonBase Options") - -## My game is crashing! -If you are having problems with your game crashing, first make sure you are running the latest version of GoonMod. Also make sure that you are not using any other Lua mods with your Payday 2. If you are using other mods such as HoxHud or PocoHud, try disabling them first before reporting any errors. - -![GoonBase Log File](http://payday.jameswilko.com/github/goonmod_logfile.jpg "GoonBase Log File") - -If you are sure your game is crashing, then send the GoonBase.log file from your Payday 2 folder to me via email (listed below). As well as what heist (and what day of the heist) you were playing, when and what you were doing when you crashed. Crash logs and detailed instructions will help me get an understanding of why the game is crashing, and will let me get it fixed ASAP for you. - -## Contact Information -If you are experiencing crashes, want to ask a few questions, talk, send me a suggestion, tell me to stop modding your game, or anything else, you can find me at one of the contact methods below. - -Email: [jw(at)jameswilko(dot)com](mailto:jw@jameswilko.com) -Website: [http://jameswilko.com/](http://jameswilko.com/) -Twitter: @_[JamesWilko](http://twitter.com/_JamesWilko) - -## FAQ -### Do I need to join a Steam Group to use or download this? -No. - -### Is this compatible with PocoHud? -Probably! Go read this quick guide made by SA member Kikas: [http://payday.jameswilko.com/goonmod-pocohud](http://payday.jameswilko.com/goonmod-pocohud) - -### Is this compatible with HoxHud? -Maybe! I'm trying to make sure everything works properly, but there are probably going to be bugs. Complain to me, complain to the HoxHud devs, complain to everybody! +---- -### How do I make my PD2Hook file work with this and other mods? -If you are trying to use two or more mods which use the PD2Hook file you will have to manually edit this file to get them to work. -Simply copy and paste everything between, and including, the '# GOONBASE' and '# END' lines into the file underneath 'PostRequireScripts:'. This will allow you to run GoonMod with other lua mods such as PocoHud. -GoonMod will automatically merge its own changes with your current file after the initial installation, so you will only have to set the file up once. +This is the development branch for GoonMod for Payday 2. It should not be used as a normal installation as there will be errors, bugs, and crashes. -### What Open-Source License does this use? -The MIT License, see the LICENSE.md. +### Installation +GoonMod requires the [Payday 2 BLT](http://paydaymods.com/download/) in order to run. +Once the BLT is installed, place the `GoonMod` folder into the `mods` folder. From 34acdbf0ea7b0e28a148e6d2f643c541236fdf87 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Mon, 23 Feb 2015 15:41:45 +1100 Subject: [PATCH 04/92] Rewrote Options to only save data set to the Options table Options are saved when menus call the ClosedGoonModOptions back callback instead of every time an item is changed --- GoonMod/goonbase.lua | 1 + GoonMod/menus/corpse_mod_menu.txt | 1 + GoonMod/mods/body_count.lua | 7 ++--- GoonMod/req/options.lua | 50 ++++++++++++++++++++++++------- 4 files changed, 44 insertions(+), 15 deletions(-) diff --git a/GoonMod/goonbase.lua b/GoonMod/goonbase.lua index e172cec..9f9b01c 100644 --- a/GoonMod/goonbase.lua +++ b/GoonMod/goonbase.lua @@ -10,6 +10,7 @@ if not _G.GoonBase then GoonBase.ModsFolder = "mods/" GoonBase.MenusPath = "menus/" GoonBase.LocalizationFolder = "loc/" + GoonBase.SavePath = SavePath .. "goonmod_options.txt" GoonBase.SupportedVersion = true GoonBase.LogTag = "[GoonMod]" end diff --git a/GoonMod/menus/corpse_mod_menu.txt b/GoonMod/menus/corpse_mod_menu.txt index 68ff0c9..a4af761 100644 --- a/GoonMod/menus/corpse_mod_menu.txt +++ b/GoonMod/menus/corpse_mod_menu.txt @@ -3,6 +3,7 @@ "parent_menu_id" : "goonbase_options_menu", "title" : "gm_options_corpse_menu_title", "description" : "gm_options_corpse_menu_desc", + "back_callback" : "ClosedGoonModOptions", "items" : [ { diff --git a/GoonMod/mods/body_count.lua b/GoonMod/mods/body_count.lua index ec0c76e..db42a12 100644 --- a/GoonMod/mods/body_count.lua +++ b/GoonMod/mods/body_count.lua @@ -18,6 +18,7 @@ end -- Body Count Mod _G.GoonBase.CorpseDelimiter = _G.GoonBase.CorpseDelimiter or {} +GoonBase.CorpseDelimiter.MenuFile = "corpse_mod_menu.txt" -- Options GoonBase.Options.BodyCount = GoonBase.Options.BodyCount or {} @@ -56,22 +57,18 @@ Hooks:Add( "MenuManagerInitialize", "MenuManagerInitialize_" .. Mod:ID(), functi -- Callbacks MenuCallbackHandler.ToggleCorpseLimit = function(this, item) GoonBase.Options.BodyCount.UseCustomCorpseLimit = item:value() == "on" and true or false - GoonBase.Options:Save() end MenuCallbackHandler.SetMaximumCorpseAmount = function(this, item) GoonBase.Options.BodyCount.MaxCorpses = math.floor( item:value() ) - GoonBase.Options:Save() end MenuCallbackHandler.ToggleDespawnShields = function(this, item) GoonBase.Options.BodyCount.RemoveShields = item:value() == "on" and true or false - GoonBase.Options:Save() end MenuCallbackHandler.SetShieldDespawnTime = function(this, item) GoonBase.Options.BodyCount.RemoveShieldsTime = math.floor( item:value() ) - GoonBase.Options:Save() end GoonBase.CorpseDelimiter.DoRemoveAllCorpses = function(self) @@ -90,6 +87,6 @@ Hooks:Add( "MenuManagerInitialize", "MenuManagerInitialize_" .. Mod:ID(), functi end - MenuHelper:LoadFromJsonFile( GoonBase.MenusPath .. "corpse_mod_menu.txt", GoonBase.CorpseDelimiter, GoonBase.Options.BodyCount ) + MenuHelper:LoadFromJsonFile( GoonBase.MenusPath .. GoonBase.CorpseDelimiter.MenuFile, GoonBase.CorpseDelimiter, GoonBase.Options.BodyCount ) end) diff --git a/GoonMod/req/options.lua b/GoonMod/req/options.lua index 47a0460..bb51d8a 100644 --- a/GoonMod/req/options.lua +++ b/GoonMod/req/options.lua @@ -1,13 +1,35 @@ _G.GoonBase.Options = _G.GoonBase.Options or {} -local Options = _G.GoonBase.Options -Options.SaveFileName = "goonmod_options.txt" -Options.SaveFilePath = SavePath .. Options.SaveFileName +local Options = GoonBase.Options + +local mt = getmetatable( Options ) +if mt == nil then + mt = {} + mt.__func = {} + mt.__data = {} + setmetatable( Options, mt ) +end + +function mt.__index(tbl, key) + return mt.__data[key] or mt.__func[key] +end + +function mt.__newindex(tbl, key, value) + mt.__data[key] = value +end local options_menu_id = "goonbase_options_menu" Hooks:RegisterHook( "MenuManagerSetupGoonBaseMenu" ) Hooks:RegisterHook( "MenuManagerPostSetupGoonBaseMenu" ) +Hooks:Add( "MenuManagerInitialize", "MenuManagerInitialize_OptionsMenu", function( menu_manager ) + + MenuCallbackHandler.ClosedGoonModOptions = function(this) + GoonBase.Options:Save() + end + +end) + Hooks:Add("MenuManagerSetupCustomMenus", "MenuManagerSetupCustomMenus_OptionsMenu", function(menu_manager, mainmenu_nodes) MenuHelper:NewMenu( options_menu_id ) end) @@ -30,35 +52,43 @@ Hooks:Add("MenuManagerBuildCustomMenus", "MenuManagerBuildCustomMenus_OptionsMen end) -function Options:Save( file_path ) +local function OptionsSave( self, file_path ) - file_path = file_path or Options.SaveFilePath + file_path = file_path or GoonBase.SavePath local file = io.open(file_path, "w+") if file then - file:write( json.encode(Options) ) + + file:write( json.encode(mt.__data) ) file:close() + else Print("[Error] Could not save GoonMod options!") end end +rawset( mt.__func, "Save", OptionsSave ) -function Options:Load( file_path ) +local function OptionsLoad( self, file_path ) - file_path = file_path or Options.SaveFilePath + file_path = file_path or GoonBase.SavePath local file = io.open( file_path, "r" ) if file then local file_contents = file:read("*all") - Options = json.decode( file_contents ) + local data = json.decode( file_contents ) file:close() - return Options + + if data then + mt.__data = data + end else Print("[Warning] Could not load file '" .. file_path .. "', no data loaded...") end end +rawset( mt.__func, "Load", OptionsLoad ) + Options:Load() From 3326e5de7759ca3fc188d8c8574acc89c7fe2c2e Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Mon, 23 Feb 2015 15:56:04 +1100 Subject: [PATCH 05/92] Removed Hooks:PCalls from HUDManager --- GoonMod/lua/HUDManager.lua | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/GoonMod/lua/HUDManager.lua b/GoonMod/lua/HUDManager.lua index ba59320..fbe6e84 100644 --- a/GoonMod/lua/HUDManager.lua +++ b/GoonMod/lua/HUDManager.lua @@ -1,24 +1,6 @@ CloneClass( HUDManager ) -Hooks:RegisterHook("HUDManagerSetStaminaValue") -function HUDManager.set_stamina_value(this, value) - this.orig.set_stamina_value(this, value) - Hooks:PCall("HUDManagerSetStaminaValue", this, value) -end - -Hooks:RegisterHook("HUDManagerSetMaxStamina") -function HUDManager.set_max_stamina(this, value) - this.orig.set_max_stamina(this, value) - Hooks:PCall("HUDManagerSetMaxStamina", this, value) -end - -Hooks:RegisterHook("HUDManagerSetMugshotDowned") -function HUDManager.set_mugshot_downed(this, id) - this.orig.set_mugshot_downed(this, id) - Hooks:PCall("HUDManagerSetMugshotDowned", this, id) -end - Hooks:RegisterHook("HUDManagerPreAddWaypoint") function HUDManager.add_waypoint(self, id, data) local r = Hooks:ReturnCall("HUDManagerPreAddWaypoint", self, id, data) From 36f74c0adf67f045868f1386b477427076cd86ae Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Mon, 23 Feb 2015 18:49:13 +1100 Subject: [PATCH 06/92] Fixed body count binds attempting to remove enemies when no enemy manager is present --- GoonMod/mods/body_count.lua | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/GoonMod/mods/body_count.lua b/GoonMod/mods/body_count.lua index db42a12..0119fa7 100644 --- a/GoonMod/mods/body_count.lua +++ b/GoonMod/mods/body_count.lua @@ -21,7 +21,7 @@ _G.GoonBase.CorpseDelimiter = _G.GoonBase.CorpseDelimiter or {} GoonBase.CorpseDelimiter.MenuFile = "corpse_mod_menu.txt" -- Options -GoonBase.Options.BodyCount = GoonBase.Options.BodyCount or {} +GoonBase.Options.BodyCount = GoonBase.Options.BodyCount or {} GoonBase.Options.BodyCount.UseCustomCorpseLimit = GoonBase.Options.BodyCount.UseCustomCorpseLimit or true GoonBase.Options.BodyCount.MaxCorpses = GoonBase.Options.BodyCount.MaxCorpses or 256 GoonBase.Options.BodyCount.RemoveShields = GoonBase.Options.BodyCount.RemoveShields or false @@ -72,17 +72,23 @@ Hooks:Add( "MenuManagerInitialize", "MenuManagerInitialize_" .. Mod:ID(), functi end GoonBase.CorpseDelimiter.DoRemoveAllCorpses = function(self) - managers.enemy:dispose_all_corpses() + if managers.enemy then + managers.enemy:dispose_all_corpses() + end end GoonBase.CorpseDelimiter.DoRemoveAllShields = function(self) - local enemy_data = managers.enemy._enemy_data - local corpses = enemy_data.corpses - for u_key, u_data in pairs(corpses) do - if u_data.unit:inventory() ~= nil then - u_data.unit:inventory():destroy_all_items() + if managers.enemy then + + local enemy_data = managers.enemy._enemy_data + local corpses = enemy_data.corpses + for u_key, u_data in pairs(corpses) do + if u_data.unit:inventory() ~= nil then + u_data.unit:inventory():destroy_all_items() + end end + end end From fd56c953dbc770427bf7ec74cb085c105e4f271d Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Mon, 23 Feb 2015 18:49:59 +1100 Subject: [PATCH 07/92] Converted Custom Waypoints mod to PD2BLT version --- GoonMod/loc/en.txt | 12 +- GoonMod/menus/custom_waypoints_menu.txt | 42 ++++ GoonMod/mods/custom_waypoints.lua | 131 +++++++++++ GoonMod/mods/disabled/custom_waypoints.lua | 259 --------------------- 4 files changed, 179 insertions(+), 265 deletions(-) create mode 100644 GoonMod/menus/custom_waypoints_menu.txt create mode 100644 GoonMod/mods/custom_waypoints.lua delete mode 100644 GoonMod/mods/disabled/custom_waypoints.lua diff --git a/GoonMod/loc/en.txt b/GoonMod/loc/en.txt index 201f1cb..7e100b3 100644 --- a/GoonMod/loc/en.txt +++ b/GoonMod/loc/en.txt @@ -25,12 +25,12 @@ "gm_options_corpse_keybind_remove_shields_title" : "Remove All Shields", "gm_options_corpse_keybind_remove_shields_desc" : "Key to clean up all shields in the level when pressed", - "OptionsMenu_CustomWaypointMenuTitle" : "Custom Waypoints", - "OptionsMenu_CustomWaypointMenuMessage" : "Change settings for your customizable waypoints", - "OptionsMenu_CustomWaypointKeybindPlace" : "Place Waypoint", - "OptionsMenu_CustomWaypointKeybindRemove" : "Remove Waypoint", - "OptionsMenu_CustomWaypointShowDistanceTitle" : "Show Distance on Waypoints", - "OptionsMenu_CustomWaypointShowDistanceMessage" : "Show how far away you are from custom waypoints", + "gm_options_custom_waypoints_menu_title" : "Custom Waypoints", + "gm_options_custom_waypoints_menu_desc" : "Change settings for your customizable waypoints", + "gm_options_custom_waypoints_place" : "Place Waypoint", + "gm_options_custom_waypoints_remove" : "Remove Waypoint", + "gm_options_custom_waypoints_distance_title" : "Show Distance on Waypoints", + "gm_options_custom_waypoints_distance_desc" : "Show how far away you are from custom waypoints", "bm_menu_extended_inv" : "Special", "bm_ex_inv_in_reserve" : "IN RESERVE", diff --git a/GoonMod/menus/custom_waypoints_menu.txt b/GoonMod/menus/custom_waypoints_menu.txt new file mode 100644 index 0000000..8c1e3a4 --- /dev/null +++ b/GoonMod/menus/custom_waypoints_menu.txt @@ -0,0 +1,42 @@ +{ + "menu_id" : "gm_options_custom_waypoints_menu", + "parent_menu_id" : "goonbase_options_menu", + "title" : "gm_options_custom_waypoints_menu_title", + "description" : "gm_options_custom_waypoints_menu_desc", + "back_callback" : "ClosedGoonModOptions", + "items" : [ + + { + "type" : "keybind", + "id" : "gm_cw_keybind_place_waypoint", + "title" : "gm_options_custom_waypoints_place", + "description" : "gm_options_custom_waypoints_place", + "keybind_id" : "CustomWaypointsPlaceWaypoint", + "func" : "DoPlaceWaypoint", + }, + { + "type" : "keybind", + "id" : "gm_cw_keybind_remove_waypoint", + "title" : "gm_options_custom_waypoints_remove", + "description" : "gm_options_custom_waypoints_remove", + "keybind_id" : "CustomWaypointsRemoveWaypoint", + "func" : "DoRemoveWaypoint", + }, + { + "type" : "divider", + "size" : 16, + }, + + { + "type" : "toggle", + "id" : "gm_cm_toggle_shield_despawn", + "title" : "gm_options_custom_waypoints_distance_title", + "description" : "gm_options_custom_waypoints_distance_desc", + "callback" : "ToggleWaypointShowDistance", + "value" : "ShowDistance", + "default_value" : false, + } + + ] + +} diff --git a/GoonMod/mods/custom_waypoints.lua b/GoonMod/mods/custom_waypoints.lua new file mode 100644 index 0000000..3126fd9 --- /dev/null +++ b/GoonMod/mods/custom_waypoints.lua @@ -0,0 +1,131 @@ + +-- Mod Definition +local Mod = class( BaseMod ) +Mod.id = "CustomWaypoints" +Mod.Name = "Custom Waypoints" +Mod.Desc = "Allows players to set waypoints for themselves and friends" +Mod.Requirements = {} +Mod.Incompatibilities = {} + +Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod:ID(), function() + GoonBase.Mods:RegisterMod( Mod ) +end) + +if not Mod:IsEnabled() then + return +end + +-- Custom Waypoints +_G.GoonBase.CustomWaypoints = _G.GoonBase.CustomWaypoints or {} +local CustomWaypoints = _G.GoonBase.CustomWaypoints +CustomWaypoints.MenuFile = "custom_waypoints_menu.txt" + +-- Network +CustomWaypoints.Network = {} +CustomWaypoints.Network.PlaceWaypoint = "CustomWaypointPlace" +CustomWaypoints.Network.RemoveWaypoint = "CustomWaypointRemove" + +-- Options +GoonBase.Options.CustomWaypoints = GoonBase.Options.CustomWaypoints or {} +GoonBase.Options.CustomWaypoints.ShowDistance = GoonBase.Options.CustomWaypoints.ShowDistance or true + +-- Menu +Hooks:Add( "MenuManagerInitialize", "MenuManagerInitialize_" .. Mod:ID(), function( menu_manager ) + + MenuCallbackHandler.ToggleWaypointShowDistance = function(this, item) + GoonBase.Options.CustomWaypoints.ShowDistance = item:value() == "on" and true or false + end + + CustomWaypoints.DoPlaceWaypoint = function(self) + if Utils:IsInGameState() then + CustomWaypoints:SetWaypoint() + end + end + + CustomWaypoints.DoRemoveWaypoint = function(self) + if Utils:IsInGameState() then + CustomWaypoints:RemoveWaypoint() + end + end + + MenuHelper:LoadFromJsonFile( GoonBase.MenusPath .. CustomWaypoints.MenuFile, CustomWaypoints, GoonBase.Options.CustomWaypoints ) + +end) + + +-- Waypoints +function CustomWaypoints:_AddWaypoint( waypoint_name, pos, color_id ) + managers.hud:add_waypoint( + "CustomWaypoint_" .. waypoint_name, + { + icon = "infamy_icon", + distance = GoonBase.Options.CustomWaypoints.ShowDistance, + position = pos, + no_sync = false, + present_timer = 0, + state = "present", + radius = 50, + color = tweak_data.preplanning_peer_colors[color_id or 1], + blend_mode = "add" + } + ) +end + +function CustomWaypoints:_RemoveWaypoint( waypoint_name ) + managers.hud:remove_waypoint("CustomWaypoint_" .. waypoint_name) +end + +function CustomWaypoints:SetWaypoint() + + if managers.player:player_unit() == nil then + return + end + + local pos = Utils:GetPlayerAimPos( managers.player:player_unit() ) + if not pos then + return + end + + CustomWaypoints:_AddWaypoint( "localplayer", pos, LuaNetworking:LocalPeerID() ) + + pos = Vector3.ToString( pos ) + LuaNetworking:SendToPeers( CustomWaypoints.Network.PlaceWaypoint, pos ) + +end + +function CustomWaypoints:RemoveWaypoint() + LuaNetworking:SendToPeers( CustomWaypoints.Network.RemoveWaypoint, "" ) + CustomWaypoints:_RemoveWaypoint( "localplayer" ) +end + +function CustomWaypoints:NetworkPlace( player, position ) + + if player then + + local ply_name = LuaNetworking:GetNameFromPeerID(player) + local pos = string.ToVector3(position) + if pos ~= nil then + CustomWaypoints:_AddWaypoint( ply_name, pos, player ) + end + + end + +end + +function CustomWaypoints:NetworkRemove(player) + local ply_name = LuaNetworking:GetNameFromPeerID(player) + CustomWaypoints:_RemoveWaypoint( ply_name ) +end + +-- Networked Data +Hooks:Add("NetworkReceivedData", "NetworkReceivedData_" .. Mod:ID(), function(sender, messageType, data) + + if messageType == CustomWaypoints.Network.PlaceWaypoint then + CustomWaypoints:NetworkPlace(sender, data) + end + + if messageType == CustomWaypoints.Network.RemoveWaypoint then + CustomWaypoints:NetworkRemove(sender) + end + +end) diff --git a/GoonMod/mods/disabled/custom_waypoints.lua b/GoonMod/mods/disabled/custom_waypoints.lua deleted file mode 100644 index 697949c..0000000 --- a/GoonMod/mods/disabled/custom_waypoints.lua +++ /dev/null @@ -1,259 +0,0 @@ - --- Mod Definition -local Mod = class( BaseMod ) -Mod.id = "CustomWaypoints" -Mod.Name = "Custom Waypoints" -Mod.Desc = "Allows players to set waypoints for themselves and friends" -Mod.Requirements = {} -Mod.Incompatibilities = {} - -Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() - GoonBase.Mods:RegisterMod( Mod ) -end) - -if not Mod:IsEnabled() then - return -end - --- Custom Waypoints -_G.GoonBase.CustomWaypoints = _G.GoonBase.CustomWaypoints or {} -local CustomWaypoints = _G.GoonBase.CustomWaypoints -CustomWaypoints.MenuID = "goonbase_custom_waypoints_menu" -CustomWaypoints.PlaceWaypointName = "GoonBasePlaceWaypoint" -CustomWaypoints.RemoveWaypointName = "GoonBaseRemoveWaypoint" -CustomWaypoints.CustomKeys = { - PLACE = GoonBase.Options.CustomWaypoints ~= nil and GoonBase.Options.CustomWaypoints.PlaceWaypoint or "k", - REMOVE = GoonBase.Options.CustomWaypoints ~= nil and GoonBase.Options.CustomWaypoints.RemoveWaypoint or "l" -} - --- Network -CustomWaypoints.Network = {} -CustomWaypoints.Network.PlaceWaypoint = "CustomWaypointPlace" -CustomWaypoints.Network.RemoveWaypoint = "CustomWaypointRemove" - --- Options -if GoonBase.Options.CustomWaypoints == nil then - GoonBase.Options.CustomWaypoints = {} - GoonBase.Options.CustomWaypoints.PlaceWaypoint = "k" - GoonBase.Options.CustomWaypoints.RemoveWaypoint = "l" - GoonBase.Options.CustomWaypoints.ShowDistance = true -end - --- Updates -Hooks:Add("GameSetupUpdate", "GameSetupUpdate_" .. Mod:ID(), function(t, dt) - CustomWaypoints:UpdateBindings() -end) - -function CustomWaypoints:UpdateBindings() - - local self = CustomWaypoints - if self._input == nil then - self._input = Input:keyboard() - end - if managers.hud:chat_focus() then - return - end - - local place_key = CustomWaypoints.CustomKeys.PLACE - if not string.is_nil_or_empty(place_key) and self._input:pressed(Idstring(place_key)) then - CustomWaypoints:SetWaypoint() - end - - local remove_key = CustomWaypoints.CustomKeys.REMOVE - if not string.is_nil_or_empty(remove_key) and self._input:pressed(Idstring(remove_key)) then - CustomWaypoints:RemoveWaypoint() - end - -end - --- Custom Key Set -Hooks:Add("CustomizeControllerOnKeySet", "CustomizeControllerOnKeySet_" .. Mod:ID(), function(item) - - if item._name == CustomWaypoints.PlaceWaypointName then - CustomWaypoints.CustomKeys.PLACE = item._input_name_list[1] - CustomWaypoints:SaveBindings() - end - - if item._name == CustomWaypoints.RemoveWaypointName then - CustomWaypoints.CustomKeys.REMOVE = item._input_name_list[1] - CustomWaypoints:SaveBindings() - end - -end) - -function CustomWaypoints:SaveBindings() - GoonBase.Options.CustomWaypoints.PlaceWaypoint = CustomWaypoints.CustomKeys.PLACE - GoonBase.Options.CustomWaypoints.RemoveWaypoint = CustomWaypoints.CustomKeys.REMOVE - GoonBase.Options:Save() -end - --- Menu -Hooks:Add("MenuManagerSetupCustomMenus", "MenuManagerSetupCustomMenus_" .. Mod:ID(), function(menu_manager, menu_nodes) - MenuHelper:NewMenu( CustomWaypoints.MenuID ) -end) - -Hooks:Add("MenuManagerSetupGoonBaseMenu", "MenuManagerSetupGoonBaseMenu_" .. Mod:ID(), function( menu_manager ) - - -- Menu button - MenuHelper:AddButton({ - id = "custom_waypoint_menu_button", - title = "OptionsMenu_CustomWaypointMenuTitle", - desc = "OptionsMenu_CustomWaypointMenuMessage", - next_node = CustomWaypoints.MenuID, - menu_id = "goonbase_options_menu" - }) - - -- Keybinds - MenuHelper:AddKeybinding({ - id = "custom_waypoint_menu_keybind_place", - title = managers.localization:text("OptionsMenu_CustomWaypointKeybindPlace"), - connection_name = CustomWaypoints.PlaceWaypointName, - button = CustomWaypoints.CustomKeys.PLACE, - binding = CustomWaypoints.CustomKeys.PLACE, - menu_id = CustomWaypoints.MenuID, - priority = 10 - }) - - MenuHelper:AddKeybinding({ - id = "custom_waypoint_menu_keybind_remove", - title = managers.localization:text("OptionsMenu_CustomWaypointKeybindRemove"), - connection_name = CustomWaypoints.RemoveWaypointName, - button = CustomWaypoints.CustomKeys.REMOVE, - binding = CustomWaypoints.CustomKeys.REMOVE, - menu_id = CustomWaypoints.MenuID, - priority = 9 - }) - - -- Show Distance - MenuCallbackHandler.toggle_custom_waypoint_distance = function(this, item) - GoonBase.Options.CustomWaypoints.ShowDistance = item:value() == "on" and true or false - GoonBase.Options:Save() - end - - MenuHelper:AddDivider({ - id = "custom_waypoint_menu_divider", - menu_id = CustomWaypoints.MenuID, - size = 16, - priority = 2, - }) - - MenuHelper:AddToggle({ - id = "toggle_custom_waypoint_distance", - title = "OptionsMenu_CustomWaypointShowDistanceTitle", - desc = "OptionsMenu_CustomWaypointShowDistanceMessage", - callback = "toggle_custom_waypoint_distance", - value = GoonBase.Options.CustomWaypoints.ShowDistance, - menu_id = CustomWaypoints.MenuID, - priority = 1, - }) - -end) - -Hooks:Add("MenuManagerBuildCustomMenus", "MenuManagerBuildCustomMenus_" .. Mod:ID(), function(menu_manager, mainmenu_nodes) - mainmenu_nodes[CustomWaypoints.MenuID] = MenuHelper:BuildMenu( CustomWaypoints.MenuID ) -end) - --- Waypoints -function CustomWaypoints:_AddWaypoint( waypoint_name, pos, color_id ) - managers.hud:add_waypoint( - "CustomWaypoint_" .. waypoint_name, - { - icon = "infamy_icon", - distance = GoonBase.Options.CustomWaypoints.ShowDistance, - position = pos, - no_sync = false, - present_timer = 0, - state = "present", - radius = 50, - color = tweak_data.preplanning_peer_colors[color_id or 1], - blend_mode = "add" - } - ) -end - -function CustomWaypoints:_RemoveWaypoint( waypoint_name ) - managers.hud:remove_waypoint("CustomWaypoint_" .. waypoint_name) -end - -function CustomWaypoints:SetWaypoint() - - if managers.player:player_unit() == nil then - return - end - - local psuccess, perror = pcall(function() - - local pos = GetPlayerAimPos( managers.player:player_unit() ) - if not pos then - return - end - - CustomWaypoints:_AddWaypoint( "localplayer", pos, GoonBase.Network:LocalPeerID() ) - - pos = Vector3.ToString( pos ) - GoonBase.Network:SendToPeers( CustomWaypoints.Network.PlaceWaypoint, pos ) - - end) - if not psuccess then - Print("[Error] " .. perror) - end - -end - -function CustomWaypoints:RemoveWaypoint() - - local psuccess, perror = pcall(function() - - GoonBase.Network:SendToPeers( CustomWaypoints.Network.RemoveWaypoint, "" ) - CustomWaypoints:_RemoveWaypoint( "localplayer" ) - - end) - if not psuccess then - Print("[Error] " .. perror) - end - -end - -function CustomWaypoints:NetworkPlace( player, position ) - - local psuccess, perror = pcall(function() - - local ply_name = GoonBase.Network:GetNameFromPeerID(player) - local pos = string.ToVector3(position) - if pos ~= nil then - CustomWaypoints:_AddWaypoint( ply_name, pos, player ) - end - - end) - if not psuccess then - Print("[Error] " .. perror) - end - -end - -function CustomWaypoints:NetworkRemove(player) - - local psuccess, perror = pcall(function() - - local ply_name = GoonBase.Network:GetNameFromPeerID(player) - CustomWaypoints:_RemoveWaypoint( ply_name ) - - end) - if not psuccess then - Print("[Error] " .. perror) - end - -end - --- Networked Data -Hooks:Add("NetworkReceivedData", "NetworkReceivedData_" .. Mod:ID(), function(sender, messageType, data) - - if messageType == CustomWaypoints.Network.PlaceWaypoint then - CustomWaypoints:NetworkPlace(sender, data) - end - - if messageType == CustomWaypoints.Network.RemoveWaypoint then - CustomWaypoints:NetworkRemove(sender) - end - -end) From 1ddd86dadd580385336ed4590adc83ad583e2577 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Tue, 24 Feb 2015 22:28:25 +1100 Subject: [PATCH 08/92] Blackmarket menu items will be split into two columns when more than 5 items are added to the menu to prevent clipping the DLC item notification --- GoonMod/lua/BlackMarketGUI.lua | 193 +++++++++++++++++++++++++++++++-- 1 file changed, 183 insertions(+), 10 deletions(-) diff --git a/GoonMod/lua/BlackMarketGUI.lua b/GoonMod/lua/BlackMarketGUI.lua index 1d90a2b..c3d7b14 100644 --- a/GoonMod/lua/BlackMarketGUI.lua +++ b/GoonMod/lua/BlackMarketGUI.lua @@ -1,5 +1,6 @@ CloneClass( BlackMarketGui ) +CloneClass( BlackMarketGuiButtonItem ) local is_win32 = SystemInfo:platform() == Idstring("WIN32") local NOT_WIN_32 = not is_win32 @@ -46,24 +47,113 @@ function BlackMarketGui.init(self, ws, fullscreen_ws, node) end self:set_enabled(true) + BlackMarketGui._instance = self + self:on_slot_selected( self._selected_slot ) + self:show_btns( self._selected_slot ) + self:_update_borders() + end Hooks:RegisterHook("BlackMarketGUIPreSetup") Hooks:RegisterHook("BlackMarketGUIPostSetup") function BlackMarketGui._setup(self, is_start_page, component_data) + Hooks:Call("BlackMarketGUIPreSetup", self, is_start_page, component_data) + self.orig._setup(self, is_start_page, component_data) + Hooks:Call("BlackMarketGUIPostSetup", self, is_start_page, component_data) +end - local psuccess, perror = pcall(function() - - Hooks:Call("BlackMarketGUIPreSetup", self, is_start_page, component_data) - self.orig._setup(self, is_start_page, component_data) - Hooks:Call("BlackMarketGUIPostSetup", self, is_start_page, component_data) - self:on_slot_selected( self._selected_slot ) +function BlackMarketGuiButtonItem.init(self, main_panel, data, x) + self.orig.init(self, main_panel, data, x) + self._main_panel = main_panel + self._initial_x = x +end - end) - if not psuccess then - Print("[Error] " .. perror) +function BlackMarketGuiButtonItem:set_order( prio ) + + BlackMarketGuiButtonItem._default_w = BlackMarketGuiButtonItem._default_w or self._panel:w() + BlackMarketGuiButtonItem._highlight_w = BlackMarketGuiButtonItem._highlight_w or self._panel:w() / 2 + BlackMarketGuiButtonItem._padding = BlackMarketGuiButtonItem._padding or 8 + BlackMarketGuiButtonItem._max_btn_height = BlackMarketGuiButtonItem._max_btn_height or 5 + local btn_h = BlackMarketGuiButtonItem._max_btn_height + local num = BlackMarketGui._instance and BlackMarketGui._instance._button_count or 0 + + if num and num > btn_h then + + self._panel:set_w( BlackMarketGuiButtonItem._highlight_w ) + self._panel:set_y( (prio % btn_h) * small_font_size ) + + if prio > btn_h then + self._panel:set_left( self._main_panel:left() + BlackMarketGuiButtonItem._padding ) + else + self._panel:set_right( self._main_panel:right() - BlackMarketGuiButtonItem._padding ) + end + + else + self._panel:set_w( BlackMarketGuiButtonItem._default_w ) + self._panel:set_y( (prio - 1) * small_font_size ) + self._panel:set_left( self._initial_x or 0 ) + end + + self._btn_text:set_right(self._panel:w()) + +end + +function BlackMarketGui._update_borders( self ) + + local wh = self._weapon_info_panel:h() + local dy = self._detection_panel:y() + local dh = self._detection_panel:h() + local by = self._btn_panel:y() + local bh = self._btn_panel:h() + local btn_h = 20 + + self._btn_panel:set_visible(self._button_count > 0 and true or false) + if self._btn_panel:visible() then + if self._button_count > BlackMarketGuiButtonItem._max_btn_height then + btn_h = btn_h / 2 + end + self._btn_panel:set_h(btn_h * self._button_count + 16) + end + + local info_box_panel = self._panel:child("info_box_panel") + local weapon_info_height = info_box_panel:h() - (self._button_count > 0 and self._btn_panel:h() + 8 or 0) - (self._detection_panel:visible() and self._detection_panel:h() + 8 or 0) + self._weapon_info_panel:set_h(weapon_info_height) + self._info_texts_panel:set_h(weapon_info_height - btn_h) + if self._detection_panel:visible() then + self._detection_panel:set_top(self._weapon_info_panel:bottom() + 8) + if dh ~= self._detection_panel:h() or dy ~= self._detection_panel:y() then + self._detection_border:create_sides(self._detection_panel, { + sides = { + 1, + 1, + 1, + 1 + } + }) + end + end + self._btn_panel:set_top((self._detection_panel:visible() and self._detection_panel:bottom() or self._weapon_info_panel:bottom()) + 8) + if wh ~= self._weapon_info_panel:h() then + self._weapon_info_border:create_sides(self._weapon_info_panel, { + sides = { + 1, + 1, + 1, + 1 + } + }) + end + if bh ~= self._btn_panel:h() or by ~= self._btn_panel:y() then + self._button_border:create_sides(self._btn_panel, { + sides = { + 1, + 1, + 1, + 1 + } + }) end - + end Hooks:RegisterHook("BlackMarketGUIOnPopulateWeapons") @@ -1481,8 +1571,91 @@ function BlackMarketGui._update_info_text(self, slot_data, updated_texts, data, self._rename_caret:set_world_position(x + w, y) end + -- info_box_panel:set_h(64) + self._info_panel:set_h(64) + end +function BlackMarketGui:mouse_pressed(button, x, y) + if not self._enabled then + return + end + if self._renaming_item then + self:_stop_rename_item() + return + end + local holding_shift = false + local scroll_button_pressed = button == Idstring("mouse wheel up") or button == Idstring("mouse wheel down") + local inside_tab_area = self._tab_area_panel:inside(x, y) + if inside_tab_area then + if button == Idstring("mouse wheel down") then + self:next_page(true) + return + elseif button == Idstring("mouse wheel up") then + self:previous_page(true) + return + end + elseif self._tabs[self._selected] and scroll_button_pressed and self._tabs[self._selected]:mouse_pressed(button, x, y) then + local x, y = self._tabs[self._selected]:selected_slot_center() + self._select_rect:set_world_center(x, y) + self._select_rect:stop() + self._select_rect_box:set_color(Color.white) + self._select_rect:set_visible(y > self._tabs[self._selected]._grid_panel:top() and y < self._tabs[self._selected]._grid_panel:bottom()) + return + end + if button ~= Idstring("0") then + return + end + if self._panel:child("back_button"):inside(x, y) then + managers.menu:back(true) + return + end + if self._tab_scroll_table.left_klick and self._tab_scroll_table.left:inside(x, y) then + self:previous_page() + return + end + if self._tab_scroll_table.right_klick and self._tab_scroll_table.right:inside(x, y) then + self:next_page() + return + end + if self._selected_slot and self._selected_slot._equipped_rect then + self._selected_slot._equipped_rect:set_alpha(1) + end + if self._tab_scroll_panel:inside(x, y) and self._tabs[self._highlighted] and self._tabs[self._highlighted]:inside(x, y) ~= 1 then + if self._selected ~= self._highlighted then + self:set_selected_tab(self._highlighted) + end + return + elseif self._tabs[self._selected] then + local selected_slot = self._tabs[self._selected]:mouse_pressed(button, x, y) + self:on_slot_selected(selected_slot) + if selected_slot then + return + end + end + if self._rename_info_text then + local text_button = self._info_texts and self._info_texts[self._rename_info_text] + if self._slot_data and text_button and text_button:inside(x, y) then + local category = self._slot_data.category + local slot = self._slot_data.slot + self:_start_rename_item(category, slot) + return + end + end + if self._btns[self._button_highlighted] and self._btns[self._button_highlighted]:inside(x, y) then + local data = self._btns[self._button_highlighted]._data + if data.callback and (not self._button_press_delay or self._button_press_delay < TimerManager:main():time()) then + managers.menu_component:post_event("menu_enter") + data.callback(self._slot_data, self._data.topic_params) + self._button_press_delay = TimerManager:main():time() + 0.2 + end + end + if self._selected_slot and self._selected_slot._equipped_rect then + self._selected_slot._equipped_rect:set_alpha(0.6) + end +end + + Hooks:RegisterHook("BlackMarketGUIOnPopulateBuyMasks") Hooks:RegisterHook("BlackMarketGUIOnPopulateBuyMasksActionList") function BlackMarketGui.populate_buy_mask(self, data) From 2ea8a396523e9eca03e0472bb2002c82736ea3c9 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Wed, 25 Feb 2015 21:20:51 +1100 Subject: [PATCH 09/92] Normalized Ironsights Sensitivity and Remember Gadget State already work with the new hook --- GoonMod/mods/{disabled => }/weapon_remember_gadget.lua | 0 GoonMod/mods/{disabled => }/zoom_sensitivity.lua | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename GoonMod/mods/{disabled => }/weapon_remember_gadget.lua (100%) rename GoonMod/mods/{disabled => }/zoom_sensitivity.lua (100%) diff --git a/GoonMod/mods/disabled/weapon_remember_gadget.lua b/GoonMod/mods/weapon_remember_gadget.lua similarity index 100% rename from GoonMod/mods/disabled/weapon_remember_gadget.lua rename to GoonMod/mods/weapon_remember_gadget.lua diff --git a/GoonMod/mods/disabled/zoom_sensitivity.lua b/GoonMod/mods/zoom_sensitivity.lua similarity index 100% rename from GoonMod/mods/disabled/zoom_sensitivity.lua rename to GoonMod/mods/zoom_sensitivity.lua From dd915fcc97c2a7add545c33a8fb743d6d7460914 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Wed, 25 Feb 2015 22:20:52 +1100 Subject: [PATCH 10/92] Cleaned up Normalized Ironsights Sensitivity --- GoonMod/mods/zoom_sensitivity.lua | 35 +++++++++++++------------------ 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/GoonMod/mods/zoom_sensitivity.lua b/GoonMod/mods/zoom_sensitivity.lua index ed4033c..74cc7b3 100644 --- a/GoonMod/mods/zoom_sensitivity.lua +++ b/GoonMod/mods/zoom_sensitivity.lua @@ -16,36 +16,31 @@ if not Mod:IsEnabled() then end -- Options -GoonBase.Options.IronsightSensitivity = GoonBase.Options.IronsightSensitivity or {} -GoonBase.Options.IronsightSensitivity.Enabled = true +GoonBase.Options.IronsightSensitivity = GoonBase.Options.IronsightSensitivity or {} +GoonBase.Options.IronsightSensitivity.Enabled = GoonBase.Options.IronsightSensitivity.Enabled or true -- Add options to menu Hooks:Add("MenuManagerSetupGoonBaseMenu", "MenuManagerSetupGoonBaseMenu_" .. Mod:ID(), function( menu_manager ) - local success, err = pcall(function() - - MenuCallbackHandler.toggle_zoom_sensitivity = function(this, item) - GoonBase.Options.IronsightSensitivity.Enabled = item:value() == "on" and true or false - GoonBase.Options:Save() - end - - MenuHelper:AddToggle({ - id = "toggle_zoom_sensitivity", - title = "OptionsMenu_ZoomSensitivityTitle", - desc = "OptionsMenu_ZoomSensitivityMessage", - callback = "toggle_zoom_sensitivity", - value = GoonBase.Options.IronsightSensitivity.Enabled, - menu_id = "goonbase_options_menu" - }) + MenuCallbackHandler.toggle_zoom_sensitivity = function(this, item) + GoonBase.Options.IronsightSensitivity.Enabled = item:value() == "on" and true or false + GoonBase.Options:Save() + end - end) - if not success then PrintTable(err) end + MenuHelper:AddToggle({ + id = "toggle_zoom_sensitivity", + title = "OptionsMenu_ZoomSensitivityTitle", + desc = "OptionsMenu_ZoomSensitivityMessage", + callback = "toggle_zoom_sensitivity", + value = GoonBase.Options.IronsightSensitivity.Enabled, + menu_id = "goonbase_options_menu" + }) end) Hooks:Add( "MenuManagerSetMouseSensitivity", "MenuManagerSetMouseSensitivity_" .. Mod:ID(), function( menu_manager, zoomed ) - if GoonBase.Options.IronsightSensitivity == nil or not GoonBase.Options.IronsightSensitivity.Enabled then + if not GoonBase.Options.IronsightSensitivity and not GoonBase.Options.IronsightSensitivity.Enabled then return end From 41a5fee2b19c1647bf0d07e280a5079ad090f939 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Wed, 25 Feb 2015 22:22:50 +1100 Subject: [PATCH 11/92] Started converting Push To Interact to new system Push to Interact now uses json-defined menu Push to Interact localization keys updated Removed old Push To Interact file --- GoonMod/loc/en.txt | 22 +- GoonMod/menus/push_to_interact_menu.txt | 65 ++++++ GoonMod/mods/disabled/push_to_interact.lua | 231 --------------------- GoonMod/mods/push_to_interact.lua | 62 ++++++ 4 files changed, 139 insertions(+), 241 deletions(-) create mode 100644 GoonMod/menus/push_to_interact_menu.txt delete mode 100644 GoonMod/mods/disabled/push_to_interact.lua create mode 100644 GoonMod/mods/push_to_interact.lua diff --git a/GoonMod/loc/en.txt b/GoonMod/loc/en.txt index 7e100b3..a4a45ca 100644 --- a/GoonMod/loc/en.txt +++ b/GoonMod/loc/en.txt @@ -137,14 +137,18 @@ "MissingMutators_Continue" : "Continue Anyway", "MissingMutators_Cancel" : "Cancel", - "OptionsMenu_PushInteractSubmenuTitle" : "Push to Interact", - "OptionsMenu_PushInteractSubmenuDesc" : "Change settings for Push to Interact", - "OptionsMenu_PushInteractEnableTitle" : "Enable Push to Interact", - "OptionsMenu_PushInteractEnableDesc" : "Enable Push to Interact, pushing the interact button will automatically hold the button until it is pushed again", - "OptionsMenu_PushInteractTimeTitle" : "Push Grace Period", - "OptionsMenu_PushInteractTimeDesc" : "Grace period of the push in seconds. Push-to-interact will only take effect if the button is held for this long", - "OptionsMenu_PushInteractHelperTitle" : "Enable Helper Indicator", - "OptionsMenu_PushInteractHelperDesc" : "Show a blue outline around the interaction timer when Push-to-interact will hold the interaction for you", + "gm_options_push_interact_menu_title" : "Push to Interact", + "gm_options_push_interact_menu_desc" : "Change settings for Push to Interact", + "gm_options_push_interact_enabled_title" : "Enable Push to Interact", + "gm_options_push_interact_enabled_desc" : "Enable Push to Interact, pushing the interact button will automatically hold the button until it is pushed again.", + "gm_options_push_interact_min_time_title" : "Minimum Interaction Time", + "gm_options_push_interact_min_time_desc" : "All interactions longer than this time, in seconds, will be held automatically.", + "gm_options_push_interact_hold_all_title" : "Hold All Interactions", + "gm_options_push_interact_hold_all_desc" : "All interactions will be held automatically, regardless of length.", + "gm_options_push_interact_use_stop_key_title" : "Use Cancel Interaction Key", + "gm_options_push_interact_use_stop_key_desc" : "Interactions can only be stopped by pressing the 'Cancel Interaction' key once they are being held for you.", + "gm_options_push_interact_stop_key_title" : "Cancel Interaction", + "gm_options_push_interact_stop_key_desc" : "Keybind to cancel the interaction.", "OptionsMenu_StatTrakSubmenuTitle" : "Stat-trak Weapons", "OptionsMenu_StatTrakSubmenuDesc" : "Weapons will track kills made with them", @@ -233,6 +237,4 @@ "Options_WorldLaserRainbowDesc" : "Enable rainbow instead of the set Hue", "Options_WorldLaserRainbowSpeedTitle" : "Rainbow Speed", "Options_WorldLaserRainbowSpeedDesc" : "Set the speed of the rainbow effect", - - "aaaaaaa" : "aaaaaaaaa", } diff --git a/GoonMod/menus/push_to_interact_menu.txt b/GoonMod/menus/push_to_interact_menu.txt new file mode 100644 index 0000000..a7c3fa5 --- /dev/null +++ b/GoonMod/menus/push_to_interact_menu.txt @@ -0,0 +1,65 @@ +{ + "menu_id" : "gm_options_push_to_interact_menu", + "parent_menu_id" : "goonbase_options_menu", + "title" : "gm_options_push_interact_menu_title", + "description" : "gm_options_push_interact_menu_desc", + "back_callback" : "ClosedGoonModOptions", + "items" : [ + + { + "type" : "toggle", + "id" : "gm_pti_toggle_push_interact", + "title" : "gm_options_push_interact_enabled_title", + "description" : "gm_options_push_interact_enabled_desc", + "callback" : "TogglePushToInteract", + "value" : "Enabled", + "default_value" : true, + }, + { + "type" : "slider", + "id" : "gm_pti_slider_interaction_min_time", + "title" : "gm_options_push_interact_min_time_title", + "description" : "gm_options_push_interact_min_time_desc", + "callback" : "SetPushToInteractMinimumTime", + "value" : "InteractionMinTime", + "default_value" : 2, + "min" : 0, + "max" : 10, + "step" : 0.5, + }, + { + "type" : "toggle", + "id" : "gm_pti_toggle_hold_all", + "title" : "gm_options_push_interact_hold_all_title", + "description" : "gm_options_push_interact_hold_all_desc", + "callback" : "ToggleHoldAllInteractions", + "value" : "HoldAllEnabled", + "default_value" : false, + }, + + { + "type" : "divider", + "size" : 16, + }, + + { + "type" : "toggle", + "id" : "gm_pti_toggle_use_stop_key", + "title" : "gm_options_push_interact_use_stop_key_title", + "description" : "gm_options_push_interact_use_stop_key_desc", + "callback" : "TogglePushInteractUseStopKey", + "value" : "UseStopKey", + "default_value" : false, + }, + { + "type" : "keybind", + "id" : "gm_pti_keybind_cancel_interact", + "title" : "gm_options_push_interact_stop_key_title", + "description" : "gm_options_push_interact_stop_key_desc", + "keybind_id" : "PushToInteractCancelCurrentInteraction", + "func" : "CancelCurrentInteraction", + } + + ] + +} diff --git a/GoonMod/mods/disabled/push_to_interact.lua b/GoonMod/mods/disabled/push_to_interact.lua deleted file mode 100644 index ae80d60..0000000 --- a/GoonMod/mods/disabled/push_to_interact.lua +++ /dev/null @@ -1,231 +0,0 @@ - -local interact_menu_id = "goonbase_pushtointeract_menu" - --- Mod Definition -local Mod = class( BaseMod ) -Mod.id = "PushToInteract" -Mod.Name = "Push to Interact" -Mod.Desc = "Push interact key to toggle interacting with an object" -Mod.Requirements = {} -Mod.Incompatibilities = {} - -Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() - GoonBase.Mods:RegisterMod( Mod ) -end) - -if not Mod:IsEnabled() then - return -end - --- Push to Interact -_G.GoonBase.PushToInteract = _G.GoonBase.PushToInteract or {} -local PushToInteract = _G.GoonBase.PushToInteract -PushToInteract.ForceKeepInteraction = { - ["corpse_alarm_pager"] = true, - ["c4_diffusible"] = true, - ["disarm_bomb"] = true, -} - --- Options -if not GoonBase.Options.PushToInteract then - GoonBase.Options.PushToInteract = {} - GoonBase.Options.PushToInteract.Enabled = true - GoonBase.Options.PushToInteract.GraceTime = 0.2 - GoonBase.Options.PushToInteract.ShowHelper = false -end - --- Functions -function PushToInteract:CreateInteractionIndicator() - - if not self._workspace and GoonBase.Options.PushToInteract.ShowHelper then - - self._workspace = Overlay:newgui():create_screen_workspace(0, 0, 1, 1) - self._interaction_radius = self._workspace:panel():w() / 12 - self._interaction_circle = CircleBitmapGuiObject:new(self._workspace:panel(), { - use_bg = false, - radius = self._interaction_radius, - sides = 64, - current = 64, - total = 64, - color = Color.white:with_alpha(1), - blend_mode = "add", - image = "guis/textures/pd2/specialization/progress_ring", - layer = 2, - x = self._workspace:panel():w() / 2 - self._interaction_radius - 0.5, - y = self._workspace:panel():h() / 2 - self._interaction_radius - 0.5, - }) - self._interaction_circle:set_current( 0 ) - - end - -end - -function PushToInteract:DestroyWorkspace() - - if self._workspace and alive(self._workspace) then - Overlay:newgui():destroy_workspace(self._workspace) - self._workspace = nil - end - -end - -function PushToInteract:ShowInteractionHelper() - if not GoonBase.Options.PushToInteract.ShowHelper then - return - end - if not self._interaction_circle then - PushToInteract:CreateInteractionIndicator() - end - self._interaction_circle:set_current( 1 ) -end - -function PushToInteract:UpdateInteractionHelper(t) - if not GoonBase.Options.PushToInteract.ShowHelper then - return - end - if not self._interaction_circle then - PushToInteract:CreateInteractionIndicator() - end - self._interaction_circle:set_current( t ) -end - -function PushToInteract:HideInteractionHelper() - if not GoonBase.Options.PushToInteract.ShowHelper then - return - end - if not self._interaction_circle then - PushToInteract:CreateInteractionIndicator() - end - self._interaction_circle:set_current( 0 ) -end - --- Hooks -Hooks:Add("PlayerStandardCheckActionInteract", "PlayerStandardCheckActionInteract_PushToInteract", function(ply, t, input) - - if not GoonBase.Options.PushToInteract.Enabled then - return - end - - local grace_time = (GoonBase.Options.PushToInteract.GraceTime or 0.2) - ply._last_interact_press_t = ply._last_interact_press_t or 0 - - if input.btn_interact_press then - - ply._last_interact_press_t = t - - if ply:_interacting() then - ply:_interupt_action_interact() - PushToInteract:HideInteractionHelper() - return false - end - - elseif input.btn_interact_release then - - local dt = t - ply._last_interact_press_t - local always_use = grace_time < 0.001 - - if managers.interaction and alive( managers.interaction:active_object() ) then - local tw = managers.interaction:active_object():interaction().tweak_data - if PushToInteract.ForceKeepInteraction[tw] then - always_use = true - end - end - - if always_use or dt >= grace_time then - return false - end - - end - - if ply._last_interact_press_t and ply:_interacting() then - local dt = t - ply._last_interact_press_t - if dt >= grace_time then - - if ply._interact_expire_t then - local x = (t - ply._last_interact_press_t) / (ply._interact_expire_t - ply._last_interact_press_t) - PushToInteract:UpdateInteractionHelper( x ) - else - PushToInteract:ShowInteractionHelper() - end - - else - PushToInteract:HideInteractionHelper() - end - else - PushToInteract:HideInteractionHelper() - end - -end) - --- Menu -Hooks:Add("MenuManagerSetupCustomMenus", "MenuManagerSetupCustomMenus_PushToInteract", function( menu_manager, menu_nodes ) - MenuHelper:NewMenu( interact_menu_id ) -end) - -Hooks:Add("MenuManagerSetupGoonBaseMenu", "MenuManagerSetupGoonBaseMenu_PushToInteract", function( menu_manager ) - - -- Add corpse mod menu button - MenuHelper:AddButton({ - id = "pushtointeract_mod_menu_button", - title = "OptionsMenu_PushInteractSubmenuTitle", - desc = "OptionsMenu_PushInteractSubmenuDesc", - next_node = interact_menu_id, - menu_id = "goonbase_options_menu" - }) - - -- Callbacks - MenuCallbackHandler.toggle_pushtointeract = function(this, item) - GoonBase.Options.PushToInteract.Enabled = item:value() == "on" and true or false - GoonBase.Options:Save() - end - - MenuCallbackHandler.set_pushtointeract_grace_period = function(this, item) - GoonBase.Options.PushToInteract.GraceTime = tonumber( item:value() ) - GoonBase.Options:Save() - end - - MenuCallbackHandler.toggle_pushtointeract_helper = function(this, item) - GoonBase.Options.PushToInteract.ShowHelper = item:value() == "on" and true or false - GoonBase.Options:Save() - end - - -- Menu - MenuHelper:AddToggle({ - id = "pushtointeract_toggle", - title = "OptionsMenu_PushInteractEnableTitle", - desc = "OptionsMenu_PushInteractEnableDesc", - callback = "toggle_pushtointeract", - value = GoonBase.Options.PushToInteract.Enabled, - menu_id = interact_menu_id, - priority = 50 - }) - - MenuHelper:AddSlider({ - id = "pushtointeract_timer_slider", - title = "OptionsMenu_PushInteractTimeTitle", - desc = "OptionsMenu_PushInteractTimeDesc", - callback = "set_pushtointeract_grace_period", - value = GoonBase.Options.PushToInteract.GraceTime, - min = 0, - max = 2, - step = 0.01, - show_value = true, - menu_id = interact_menu_id, - priority = 49 - }) - - MenuHelper:AddToggle({ - id = "pushtointeract_toggle_showhelper", - title = "OptionsMenu_PushInteractHelperTitle", - desc = "OptionsMenu_PushInteractHelperDesc", - callback = "toggle_pushtointeract_helper", - value = GoonBase.Options.PushToInteract.ShowHelper or false, - menu_id = interact_menu_id, - priority = 48 - }) - -end) - -Hooks:Add("MenuManagerBuildCustomMenus", "MenuManagerBuildCustomMenus_PushToInteract", function( menu_manager, mainmenu_nodes ) - mainmenu_nodes[interact_menu_id] = MenuHelper:BuildMenu( interact_menu_id ) -end) diff --git a/GoonMod/mods/push_to_interact.lua b/GoonMod/mods/push_to_interact.lua new file mode 100644 index 0000000..619691a --- /dev/null +++ b/GoonMod/mods/push_to_interact.lua @@ -0,0 +1,62 @@ + +local interact_menu_id = "goonbase_pushtointeract_menu" + +-- Mod Definition +local Mod = class( BaseMod ) +Mod.id = "PushToInteract" +Mod.Name = "Push to Interact" +Mod.Desc = "Push interact key to toggle interacting with an object." +Mod.Requirements = {} +Mod.Incompatibilities = {} + +Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() + GoonBase.Mods:RegisterMod( Mod ) +end) + +if not Mod:IsEnabled() then + return +end + +-- Push to Interact +_G.GoonBase.PushToInteract = _G.GoonBase.PushToInteract or {} +GoonBase.PushToInteract.MenuFile = "push_to_interact_menu.txt" +GoonBase.PushToInteract.ForceKeepInteraction = { + ["corpse_alarm_pager"] = true, + ["c4_diffusible"] = true, + ["disarm_bomb"] = true, +} + +-- Options +GoonBase.Options.PushToInteract = GoonBase.Options.PushToInteract or {} +GoonBase.Options.PushToInteract.Enabled = GoonBase.Options.PushToInteract.Enabled or true +GoonBase.Options.PushToInteract.InteractionMinTime = GoonBase.Options.PushToInteract.InteractionMinTime or 2 +GoonBase.Options.PushToInteract.HoldAllEnabled = GoonBase.Options.PushToInteract.HoldAllEnabled or false +GoonBase.Options.PushToInteract.UseStopKey = GoonBase.Options.PushToInteract.UseStopKey or false + +-- Menu +Hooks:Add("MenuManagerInitialize", "MenuManagerInitialize_" .. Mod:ID(), function( menu_manager ) + + -- Callbacks + MenuCallbackHandler.TogglePushToInteract = function(this, item) + GoonBase.Options.PushToInteract.Enabled = item:value() == "on" and true or false + end + + MenuCallbackHandler.SetPushToInteractMinimumTime = function( this, item ) + GoonBase.Options.PushToInteract.InteractionMinTime = tonumber( item:value() ) + end + + MenuCallbackHandler.ToggleHoldAllInteractions = function( this, item ) + GoonBase.Options.PushToInteract.HoldAllEnabled = item:value() == "on" and true or false + end + + MenuCallbackHandler.TogglePushInteractUseStopKey = function( this, item ) + GoonBase.Options.PushToInteract.UseStopKey = item:value() == "on" and true or false + end + + GoonBase.PushToInteract.CancelCurrentInteraction = function( self ) + log("Cancel Interaction") + end + + MenuHelper:LoadFromJsonFile( GoonBase.MenusPath .. GoonBase.PushToInteract.MenuFile, GoonBase.PushToInteract, GoonBase.Options.PushToInteract ) + +end) From bd6b60406b4672e67fc2f0ce8412c1145d9ab6f3 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Sat, 28 Feb 2015 01:26:08 +1100 Subject: [PATCH 12/92] New push-to-interact functionality --- GoonMod/mods/push_to_interact.lua | 74 ++++++++++++++++++++++++++++--- 1 file changed, 69 insertions(+), 5 deletions(-) diff --git a/GoonMod/mods/push_to_interact.lua b/GoonMod/mods/push_to_interact.lua index 619691a..923e8ff 100644 --- a/GoonMod/mods/push_to_interact.lua +++ b/GoonMod/mods/push_to_interact.lua @@ -19,8 +19,9 @@ end -- Push to Interact _G.GoonBase.PushToInteract = _G.GoonBase.PushToInteract or {} -GoonBase.PushToInteract.MenuFile = "push_to_interact_menu.txt" -GoonBase.PushToInteract.ForceKeepInteraction = { +local PushToInteract = GoonBase.PushToInteract +PushToInteract.MenuFile = "push_to_interact_menu.txt" +PushToInteract.ForceKeepInteraction = { ["corpse_alarm_pager"] = true, ["c4_diffusible"] = true, ["disarm_bomb"] = true, @@ -53,10 +54,73 @@ Hooks:Add("MenuManagerInitialize", "MenuManagerInitialize_" .. Mod:ID(), functio GoonBase.Options.PushToInteract.UseStopKey = item:value() == "on" and true or false end - GoonBase.PushToInteract.CancelCurrentInteraction = function( self ) - log("Cancel Interaction") + PushToInteract.CancelCurrentInteraction = function( self ) + + if managers.player and managers.player:local_player() then + local ply = managers.player:local_player():movement()._current_state + if ply and ply:_interacting() and PushToInteract:ShouldUseStopKey() then + ply:_interupt_action_interact() + end + end + + end + + MenuHelper:LoadFromJsonFile( GoonBase.MenusPath .. PushToInteract.MenuFile, PushToInteract, GoonBase.Options.PushToInteract ) + +end) + +-- Hooks +Hooks:Add("PlayerStandardCheckActionInteract", "PlayerStandardCheckActionInteract_PushToInteract", function(ply, t, input) + + if not PushToInteract:IsEnabled() then + return nil end - MenuHelper:LoadFromJsonFile( GoonBase.MenusPath .. GoonBase.PushToInteract.MenuFile, GoonBase.PushToInteract, GoonBase.Options.PushToInteract ) + if input.btn_interact_press then + + if ply:_interacting() and not PushToInteract:ShouldUseStopKey() then + ply:_interupt_action_interact() + return false + end + + elseif input.btn_interact_release then + + local data = nil + if managers.interaction and alive( managers.interaction:active_object() ) then + data = managers.interaction:active_object():interaction().tweak_data + end + + if PushToInteract:ShouldHoldInteraction( data ) then + return false + end + + end end) + +function PushToInteract:IsEnabled() + return GoonBase.Options.PushToInteract.Enabled or true +end + +function PushToInteract:ShouldHoldAllInteractions() + return GoonBase.Options.PushToInteract.HoldAllEnabled or false +end + +function PushToInteract:MinimumInteractionTime() + return GoonBase.Options.PushToInteract.InteractionMinTime or 2 +end + +function PushToInteract:ShouldUseStopKey() + return GoonBase.Options.PushToInteract.UseStopKey or false +end + +function PushToInteract:ShouldHoldInteraction( interaction_data ) + + if PushToInteract:IsEnabled() and interaction_data then + local hold_all = PushToInteract:ShouldHoldAllInteractions() + local interaction = (interaction_data.timer or 10) >= PushToInteract:MinimumInteractionTime() + local forced_hold = PushToInteract.ForceKeepInteraction[ interaction_data ] + return hold_all or interaction or forced_hold + end + +end From 03a45a05508cd740849cf9e7f4be3d7d8963b9a5 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Sat, 28 Feb 2015 01:32:52 +1100 Subject: [PATCH 13/92] Cleaned up path variables --- GoonMod/goonbase.lua | 23 +++++++++++++++++------ GoonMod/req/localization.lua | 2 +- GoonMod/req/mods.lua | 5 ++--- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/GoonMod/goonbase.lua b/GoonMod/goonbase.lua index 9f9b01c..7f4f577 100644 --- a/GoonMod/goonbase.lua +++ b/GoonMod/goonbase.lua @@ -1,18 +1,24 @@ if not _G.GoonBase then + _G.GoonBase = {} + GoonBase.Version = 26 GoonBase.GameVersion = "1.24.3" - GoonBase.LogFile = "GoonMod.txt" + GoonBase.SupportedVersion = true + GoonBase.Path = "" GoonBase.LuaPath = "lua/" GoonBase.RequiresFolder = "req/" GoonBase.ModsFolder = "mods/" GoonBase.MenusPath = "menus/" GoonBase.LocalizationFolder = "loc/" + + GoonBase.LogFile = "GoonMod.txt" GoonBase.SavePath = SavePath .. "goonmod_options.txt" - GoonBase.SupportedVersion = true + GoonBase.LogTag = "[GoonMod]" + end GoonBase.RequireHookFiles = { @@ -137,7 +143,12 @@ if not GoonBase.HasLoadedScripts then GoonBase.Path = ModPath GoonBase.LogFile = LogsPath .. GoonBase.LogFile + + GoonBase.LuaPath = ModPath .. GoonBase.LuaPath + GoonBase.RequiresFolder = ModPath .. GoonBase.RequiresFolder + GoonBase.ModsFolder = ModPath .. GoonBase.ModsFolder GoonBase.MenusPath = ModPath .. GoonBase.MenusPath + GoonBase.LocalizationFolder = ModPath .. GoonBase.LocalizationFolder -- Check required classes exist now if class and Application and string.split then @@ -145,9 +156,9 @@ if not GoonBase.HasLoadedScripts then GoonBase.HasLoadedScripts = true -- Load required files - local required_files = file.GetFiles( GoonBase.Path .. GoonBase.RequiresFolder ) + local required_files = file.GetFiles( GoonBase.RequiresFolder ) for k, v in ipairs( required_files ) do - SafeDoFile( GoonBase.Path .. GoonBase.RequiresFolder .. v ) + SafeDoFile( GoonBase.RequiresFolder .. v ) end -- Run hooks @@ -176,10 +187,10 @@ if RequiredScript then if type( GoonBase.HookFiles[requiredScript] ) == "table" then for k, v in pairs( GoonBase.HookFiles[requiredScript] ) do - SafeDoFile( GoonBase.Path .. GoonBase.LuaPath .. v ) + SafeDoFile( GoonBase.LuaPath .. v ) end else - SafeDoFile( GoonBase.Path .. GoonBase.LuaPath .. GoonBase.HookFiles[requiredScript] ) + SafeDoFile( GoonBase.LuaPath .. GoonBase.HookFiles[requiredScript] ) end end diff --git a/GoonMod/req/localization.lua b/GoonMod/req/localization.lua index 1e4b1a4..deb7815 100644 --- a/GoonMod/req/localization.lua +++ b/GoonMod/req/localization.lua @@ -1,4 +1,4 @@ Hooks:Add("LocalizationManagerPostInit", "LocalizationManagerPostInit_LocExample", function(loc) - loc:load_localization_file( GoonBase.Path .. GoonBase.LocalizationFolder .. "en.txt" ) + loc:load_localization_file( GoonBase.LocalizationFolder .. "en.txt" ) end) diff --git a/GoonMod/req/mods.lua b/GoonMod/req/mods.lua index f6f51b5..2fd9095 100644 --- a/GoonMod/req/mods.lua +++ b/GoonMod/req/mods.lua @@ -94,11 +94,10 @@ function Mods:LoadMods() if GoonBase.SupportedVersion then - local mods_folder_path = GoonBase.Path .. GoonBase.ModsFolder - GoonBase.ModFiles = file.GetFiles( mods_folder_path ) + GoonBase.ModFiles = file.GetFiles( GoonBase.ModsFolder ) for k, v in pairs( GoonBase.ModFiles ) do - SafeDoFile( mods_folder_path .. v ) + SafeDoFile( GoonBase.ModsFolder .. v ) end end From a94bf8bac81a0ee70b185b4f232117b7f222ec66 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Sat, 28 Feb 2015 01:43:35 +1100 Subject: [PATCH 14/92] Re-implemented major version check, will disable mods on major version update 1.xx.0 --- GoonMod/goonbase.lua | 8 +++++--- GoonMod/req/autils.lua | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/GoonMod/goonbase.lua b/GoonMod/goonbase.lua index 7f4f577..3ed1342 100644 --- a/GoonMod/goonbase.lua +++ b/GoonMod/goonbase.lua @@ -4,7 +4,7 @@ if not _G.GoonBase then _G.GoonBase = {} GoonBase.Version = 26 - GoonBase.GameVersion = "1.24.3" + GoonBase.GameVersion = "1.25.0" GoonBase.SupportedVersion = true GoonBase.Path = "" @@ -161,8 +161,10 @@ if not GoonBase.HasLoadedScripts then SafeDoFile( GoonBase.RequiresFolder .. v ) end + GoonBase.SupportedVersion = GoonBase.Utils:GameUpdateVersionCheck() + -- Run hooks - if Hooks ~= nil then + if GoonBase.SupportedVersion and Hooks ~= nil then Hooks:RegisterHook("GoonBaseLoadMods") Hooks:Call("GoonBaseLoadMods") @@ -183,7 +185,7 @@ if RequiredScript then local requiredScript = RequiredScript:lower() if GoonBase.HookFiles[requiredScript] then - if GoonBase.SupportedVersion or (not GoonBase.SupportedVersion and table.contains(GoonBase.RequireHookFiles, requiredScript)) then + if GoonBase.SupportedVersion or table.contains(GoonBase.RequireHookFiles, requiredScript) then if type( GoonBase.HookFiles[requiredScript] ) == "table" then for k, v in pairs( GoonBase.HookFiles[requiredScript] ) do diff --git a/GoonMod/req/autils.lua b/GoonMod/req/autils.lua index 54b16b9..a3f1584 100644 --- a/GoonMod/req/autils.lua +++ b/GoonMod/req/autils.lua @@ -1,6 +1,25 @@ _G.GoonBase.Utils = _G.GoonBase.Utils or {} +function GoonBase.Utils:GameUpdateVersionCheck() + + local mod_version = GoonBase.GameVersion:split("[.]") + local game_version = Application:version():split("[.]") + + if not mod_version or not game_version then + return false + end + + for i = 1, 2, 1 do + if mod_version[i] < game_version[i] then + return false + end + end + + return true + +end + -- Custom "Base64" Implementation _G.GoonBase.Utils.Base64 = _G.GoonBase.Utils.Base64 or {} local Base64 = _G.GoonBase.Utils.Base64 From 6ec12f4478eec6e6e4a7a8af7a9c339e0686f568 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Sat, 28 Feb 2015 21:13:31 +1100 Subject: [PATCH 15/92] Removed test code from BlackMarketGUI --- GoonMod/lua/BlackMarketGUI.lua | 3 --- 1 file changed, 3 deletions(-) diff --git a/GoonMod/lua/BlackMarketGUI.lua b/GoonMod/lua/BlackMarketGUI.lua index c3d7b14..ecdfe19 100644 --- a/GoonMod/lua/BlackMarketGUI.lua +++ b/GoonMod/lua/BlackMarketGUI.lua @@ -1571,9 +1571,6 @@ function BlackMarketGui._update_info_text(self, slot_data, updated_texts, data, self._rename_caret:set_world_position(x + w, y) end - -- info_box_panel:set_h(64) - self._info_panel:set_h(64) - end function BlackMarketGui:mouse_pressed(button, x, y) From 2deb14abacad8d3178ba84a7778028fc754b428a Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Sat, 28 Feb 2015 21:16:08 +1100 Subject: [PATCH 16/92] Added disabled due to game updates notification Added disabled due to game updates button to mods menu Reworked options and mods menu localization keys --- GoonMod/loc/en.txt | 27 ++++++++++++++--------- GoonMod/req/mods.lua | 47 ++++++++++++++++++++++++++++++++++------- GoonMod/req/options.lua | 2 +- 3 files changed, 57 insertions(+), 19 deletions(-) diff --git a/GoonMod/loc/en.txt b/GoonMod/loc/en.txt index a4a45ca..7660b8f 100644 --- a/GoonMod/loc/en.txt +++ b/GoonMod/loc/en.txt @@ -1,14 +1,21 @@ { - "GoonBaseOptionsName" : "GoonMod", - "GoonBaseOptionsDesc" : "Change your GoonMod preferences", - - "ModsMenu_Button" : "Modifications", - "ModsMenu_ButtonDesc" : "Control which modifications are loaded", - "ModsMenu_ButtonInfoButton" : "Help", - "ModsMenu_ButtonInfoButtonDesc" : "Show the modifications menu help", - "ModsMenu_ButtonInfoButtonTitle" : "Modifications", - "ModsMenu_ButtonInfoButtonMessage" : "This menu allows you to enable and disable specific modifications in GoonMod. If a modification is enabled, it will load itself when Payday 2 is launched.\nModifications highlighted in red require another modification to be enabled before they can be loaded, these modifications will be shown at the top of the screen.\nOnce a modification is enabled/disabled, you will be required to restart your game to ensure that the modifications fully and successfully load or unload.", - "ModsMenu_ButtonInfoButtonAccept" : "Close", + "gm_options_menu" : "GoonMod", + "gm_options_menu_desc" : "Change your GoonMod preferences", + + "gm_mods_menu" : "Modifications", + "gm_mods_menu_desc" : "Control which modifications are loaded", + "gm_mods_menu_info" : "Help", + "gm_mods_menu_info_desc" : "Show the modifications menu help", + + "gm_mods_info_popup_title" : "Modifications", + "gm_mods_info_popup_message" : "This menu allows you to enable and disable specific modifications in GoonMod. If a modification is enabled, it will load itself when Payday 2 is launched.\nModifications highlighted in red require another modification to be enabled before they can be loaded, these modifications will be shown at the top of the screen.\nOnce a modification is enabled/disabled, you will be required to restart your game to ensure that the modifications fully and successfully load or unload.", + "gm_mods_info_popup_accept" : "Close", + + "gm_notify_disable_game_update" : "GoonMod Disabled!", + "gm_notify_disable_game_update_message" : "GoonMod has been disabled due to a game update.\nPlease wait for an update to be released to use it again!", + + "gm_mods_menu_disabled" : "All Modifications Disabled", + "gm_mods_menu_disabled_desc" : "All GoonMod modifications have been disabled due to a game update. Either wait for a game update, or bypass the update-lock from the options menu at your own risk.", "gm_options_corpse_menu_title" : "Corpses", "gm_options_corpse_menu_desc" : "Change settings for the ingame corpses", diff --git a/GoonMod/req/mods.lua b/GoonMod/req/mods.lua index 2fd9095..59b0876 100644 --- a/GoonMod/req/mods.lua +++ b/GoonMod/req/mods.lua @@ -22,8 +22,8 @@ Hooks:Add("MenuManagerSetupGoonBaseMenu", "MenuManagerSetupGoonBaseMenu_ModsMenu -- Options menu MenuHelper:AddButton({ id = "goonbase_mods_menu_button", - title = "ModsMenu_Button", - desc = "ModsMenu_ButtonDesc", + title = "gm_mods_menu", + desc = "gm_mods_menu_desc", next_node = Mods.MenuID, menu_id = "goonbase_options_menu", priority = 1002, @@ -43,8 +43,8 @@ Hooks:Add("MenuManagerSetupGoonBaseMenu", "MenuManagerSetupGoonBaseMenu_ModsMenu MenuHelper:AddButton({ id = "goonbase_mods_menu_help_button", - title = "ModsMenu_ButtonInfoButton", - desc = "ModsMenu_ButtonInfoButtonDesc", + title = "gm_mods_menu_info", + desc = "gm_mods_menu_info_desc", callback = "open_mods_menu_help", menu_id = Mods.MenuID, priority = 1004, @@ -77,11 +77,14 @@ end) function Mods:ShowHelpMenu() - local title = managers.localization:text("ModsMenu_ButtonInfoButtonTitle") - local message = managers.localization:text("ModsMenu_ButtonInfoButtonMessage") + local title = managers.localization:text("gm_mods_info_popup_title") + local message = managers.localization:text("gm_mods_info_popup_message") local menu_options = {} - menu_options[1] = { text = managers.localization:text("ModsMenu_ButtonInfoButtonAccept"), is_cancel_button = true } - local help_menu = QuickMenu:new(title, message, menu_options, true) + menu_options[1] = { + text = managers.localization:text("gm_mods_info_popup_accept"), + is_cancel_button = true + } + local help_menu = QuickMenu:new( title, message, menu_options, true ) end @@ -120,6 +123,17 @@ function Mods:AddLoadedModsToMenu() end end + if not GoonBase.SupportedVersion then + MenuHelper:AddButton({ + id = "goonbase_mods_menu_mods_disabled", + title = "gm_mods_menu_disabled", + desc = "gm_mods_menu_disabled_desc", + disabled = true, + menu_id = Mods.MenuID, + priority = 1000, + }) + end + end function Mods:EnableMod( mod, enabled ) @@ -181,6 +195,23 @@ Hooks:Add("LocalizationManagerPostInit", "LocalizationManagerPostInit_ModLoader" end) +Hooks:Add("MenuManagerOnOpenMenu", "MenuManagerOnOpenMenu_GoonMod", function( menu_manager, menu, position ) + + if menu == "menu_main" then + if not GoonBase.SupportedVersion then + + local id = "goonmod_mod_disabled_game_update" + local title = managers.localization:text("gm_notify_disable_game_update") + local message = managers.localization:text("gm_notify_disable_game_update_message") + local priority = 901 + + NotificationsManager:AddNotification( id, title, message, priority ) + + end + end + +end) + -- Base Mod Definition BaseMod = class() BaseMod.id = "BaseMod" diff --git a/GoonMod/req/options.lua b/GoonMod/req/options.lua index bb51d8a..f7ef825 100644 --- a/GoonMod/req/options.lua +++ b/GoonMod/req/options.lua @@ -44,7 +44,7 @@ Hooks:Add("MenuManagerBuildCustomMenus", "MenuManagerBuildCustomMenus_OptionsMen if mainmenu_nodes[mod_options_menu] then mainmenu_nodes[options_menu_id] = MenuHelper:BuildMenu( options_menu_id ) - MenuHelper:AddMenuItem( mainmenu_nodes[mod_options_menu], options_menu_id, "GoonBaseOptionsName", "GoonBaseOptionsDesc" ) + MenuHelper:AddMenuItem( mainmenu_nodes[mod_options_menu], options_menu_id, "gm_options_menu", "gm_options_menu_desc" ) Hooks:Call( "MenuManagerPostSetupGoonBaseMenu", menu_manager, mainmenu_nodes ) From f84f58bfe5b4252cc1d8ad0191589eaff2ba3d63 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Sat, 28 Feb 2015 21:17:49 +1100 Subject: [PATCH 17/92] Reworked extended inventory for BLT hook Removed protected calls Added backup loader to load old format inventory.ini so that people don't lose inventory data on conversion to new version --- .../{disabled => }/extended_inventory.lua | 158 ++++++++---------- 1 file changed, 66 insertions(+), 92 deletions(-) rename GoonMod/mods/{disabled => }/extended_inventory.lua (67%) diff --git a/GoonMod/mods/disabled/extended_inventory.lua b/GoonMod/mods/extended_inventory.lua similarity index 67% rename from GoonMod/mods/disabled/extended_inventory.lua rename to GoonMod/mods/extended_inventory.lua index 175f4fd..6c3b30e 100644 --- a/GoonMod/mods/disabled/extended_inventory.lua +++ b/GoonMod/mods/extended_inventory.lua @@ -21,7 +21,8 @@ local ExtendedInv = _G.GoonBase.ExtendedInventory ExtendedInv.InitialLoadComplete = false ExtendedInv.RegisteredItems = {} ExtendedInv.Items = {} -ExtendedInv.SaveFile = GoonBase.Path .. "inventory.ini" +ExtendedInv.SaveFile = SavePath .. "goonbase_inventory.txt" +ExtendedInv.OldFormatSaveFile = SavePath .. "inventory.ini" -- Initialize Hooks:RegisterHook("ExtendedInventoryInitialized") @@ -29,6 +30,7 @@ Hooks:Add("GoonBasePostLoadedMods", "GoonBasePostLoadedMods_ExtendedInv", functi Hooks:Call("ExtendedInventoryInitialized") end) +-- Functions function ExtendedInv:_MissingItemError(item) Print("[Error] Could not find item '" .. item .. "' in Extended Inventory!") end @@ -100,14 +102,13 @@ function ExtendedInv:GetReserveText(item) return item.reserve_text or managers.localization:text("bm_ex_inv_in_reserve") end +-- Hooks Hooks:Add("BlackMarketGUIPostSetup", "BlackMarketGUIPostSetup_ExtendedInventory", function(gui, is_start_page, component_data) gui.identifiers.extended_inv = Idstring("extended_inv") end) function ExtendedInv.do_populate_extended_inventory(self, data) - local psuccess, perror = pcall(function() - local new_data = {} local guis_catalog = "guis/" local index = 0 @@ -158,131 +159,104 @@ function ExtendedInv.do_populate_extended_inventory(self, data) end end - end) - if not psuccess then - Print("[Error] " .. perror) - end - end Hooks:Add("BlackMarketGUIStartPageData", "BlackMarketGUIStartPageData_ExtendedInventory", function(gui, data) - local psuccess, perror = pcall(function() - - local should_hide_tab = true - for k, v in pairs( ExtendedInv:GetAllItems() ) do - if v.hide_when_none_in_stock == false or (v.hide_when_none_in_stock == true and v.amount > 0) then - should_hide_tab = false - end + local should_hide_tab = true + for k, v in pairs( ExtendedInv:GetAllItems() ) do + if v.hide_when_none_in_stock == false or (v.hide_when_none_in_stock == true and v.amount > 0) then + should_hide_tab = false end - if should_hide_tab then - return - end - - gui.populate_extended_inventory = ExtendedInv.do_populate_extended_inventory + end + if should_hide_tab then + return + end - table.insert(data, { - name = "bm_menu_extended_inv", - category = "extended_inv", - on_create_func_name = "populate_extended_inventory", - identifier = gui.identifiers.extended_inv, - override_slots = {5, 2}, - start_crafted_item = 1 - }) + gui.populate_extended_inventory = ExtendedInv.do_populate_extended_inventory - end) - if not psuccess then - Print("[Error] " .. perror) - end + table.insert(data, { + name = "bm_menu_extended_inv", + category = "extended_inv", + on_create_func_name = "populate_extended_inventory", + identifier = gui.identifiers.extended_inv, + override_slots = {5, 2}, + start_crafted_item = 1 + }) end) Hooks:Add("BlackMarketGUIUpdateInfoText", "BlackMarketGUIUpdateInfoText_ExtendedInventory", function(gui) - local psuccess, perror = pcall(function() - - local self = gui - local slot_data = self._slot_data - local tab_data = self._tabs[self._selected]._data - local prev_data = tab_data.prev_node_data - local ids_category = Idstring(slot_data.category) - local identifier = tab_data.identifier - local updated_texts = { - {text = ""}, - {text = ""}, - {text = ""}, - {text = ""}, - {text = ""} - } - - if ids_category == self.identifiers.extended_inv then - - updated_texts[1].text = slot_data.name_localized or "" - updated_texts[2].text = tostring(slot_data.amount or 0) .. " " .. ExtendedInv:GetReserveText(slot_data.name) - updated_texts[4].text = slot_data.desc_localized or "" - - gui:_update_info_text(slot_data, updated_texts) - - end + local self = gui + local slot_data = self._slot_data + local tab_data = self._tabs[self._selected]._data + local prev_data = tab_data.prev_node_data + local ids_category = Idstring(slot_data.category) + local identifier = tab_data.identifier + local updated_texts = { + {text = ""}, + {text = ""}, + {text = ""}, + {text = ""}, + {text = ""} + } + + if ids_category == self.identifiers.extended_inv then + + updated_texts[1].text = slot_data.name_localized or "" + updated_texts[2].text = tostring(slot_data.amount or 0) .. " " .. ExtendedInv:GetReserveText(slot_data.name) + updated_texts[4].text = slot_data.desc_localized or "" + + gui:_update_info_text(slot_data, updated_texts) - end) - if not psuccess then - Print("[Error] " .. perror) end end) -- Saving and Loading -function ExtendedInv:GetSaveString() - - local contents = ""; - for k, v in pairs( ExtendedInv.Items ) do - - if type(v) == "table" then - contents = string.format( "%s[%s]\n", contents, tostring(k) ) - for a, b in pairs( v ) do - contents = string.format( "%s%s=%s\n", contents, tostring(a), tostring(b) ) - end - end +function ExtendedInv:Save( file_name ) + if file_name == nil then + file_name = ExtendedInv.SaveFile end - return contents + local file = io.open(file_name, "w+") + local data = json.encode( ExtendedInv.Items ) + data = GoonBase.Utils.Base64:Encode( data ) + file:write( data ) + file:close() end -function ExtendedInv:Save(fileName) +function ExtendedInv:Load( file_name ) - if fileName == nil then - fileName = ExtendedInv.SaveFile + file_name = file_name or ExtendedInv.SaveFile + local file = io.open(file_name, 'r') + if not file then + Print( "Could not open GoonMod Extended Inventory save file, attempting to load old format..." ) + ExtendedInv:LoadOldFormat() + return end - -- Encode data using "base64" while saving - -- Simple, but will stop most players from editing the save file since it'll look like gibberish - -- Those who want to cheat that badly will decode any encryption or use lua to bypass it, so - -- no point in super-complicated systems - local file = io.open(fileName, "w+") - local data = ExtendedInv:GetSaveString() - data = GoonBase.Utils.Base64:Encode( data ) - file:write( data ) - file:close() + local file_data = file:read("*all") + file_data = GoonBase.Utils.Base64:Decode( data ) + ExtendedInv.Items = json.decode( file_data ) end -function ExtendedInv:Load(fileName) +function ExtendedInv:LoadOldFormat( file_name ) - if fileName == nil then - fileName = ExtendedInv.SaveFile - end + file_name = file_name or ExtendedInv.OldFormatSaveFile - local file = io.open(fileName, 'r') - local key + local file = io.open(file_name, 'r') - if file == nil then - Print( "Could not open file (" .. fileName .. ")! Does it exist?" ) + if not file then + Print( "Could not open old format save file (" .. file_name .. ")! Does it exist?" ) return end + local key local fileString = "" for line in file:lines() do fileString = fileString .. line .. "\n" From 4cb98e838d94067538467488006b5a6ca7e4214e Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Sun, 1 Mar 2015 16:53:14 +1100 Subject: [PATCH 18/92] Removed chat manager hooks --- GoonMod/goonbase.lua | 2 -- GoonMod/mod.txt | 1 - GoonMod/req/mods.lua | 2 +- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/GoonMod/goonbase.lua b/GoonMod/goonbase.lua index 3ed1342..7c2dfcf 100644 --- a/GoonMod/goonbase.lua +++ b/GoonMod/goonbase.lua @@ -29,7 +29,6 @@ GoonBase.RequireHookFiles = { GoonBase.HookFiles = { ["lib/managers/menumanager"] = "MenuManager.lua", - ["lib/managers/chatmanager"] = "ChatManager.lua", ["lib/managers/enemymanager"] = "EnemyManager.lua", ["lib/units/weapons/grenades/quicksmokegrenade"] = "QuickSmokeGrenade.lua", ["lib/managers/hudmanager"] = "HUDManager.lua", @@ -179,7 +178,6 @@ if not GoonBase.HasLoadedScripts then end -- Load Hook Scripts - if RequiredScript then local requiredScript = RequiredScript:lower() diff --git a/GoonMod/mod.txt b/GoonMod/mod.txt index 12750de..eaca2ef 100644 --- a/GoonMod/mod.txt +++ b/GoonMod/mod.txt @@ -18,7 +18,6 @@ "hooks" : [ { "hook_id" : "lib/managers/localizationmanager", "script_path" : "goonbase.lua" }, { "hook_id" : "lib/managers/menumanager", "script_path" : "goonbase.lua" }, - { "hook_id" : "lib/managers/chatmanager", "script_path" : "goonbase.lua" }, { "hook_id" : "lib/managers/enemymanager", "script_path" : "goonbase.lua" }, { "hook_id" : "lib/units/weapons/grenades/quicksmokegrenade", "script_path" : "goonbase.lua" }, { "hook_id" : "lib/managers/hudmanager", "script_path" : "goonbase.lua" }, diff --git a/GoonMod/req/mods.lua b/GoonMod/req/mods.lua index 59b0876..1ed7374 100644 --- a/GoonMod/req/mods.lua +++ b/GoonMod/req/mods.lua @@ -217,7 +217,7 @@ BaseMod = class() BaseMod.id = "BaseMod" BaseMod.Name = "Base Modification" BaseMod.Desc = "The Base Modification" -BaseMod.MenuPrefix = "toggle_mod_" +BaseMod.MenuPrefix = "gm_mods_toggle_" BaseMod.MenuSuffix = "" BaseMod.HideInOptionsMenu = false BaseMod.Requirements = {} From a325c8dc71b7c7481d25340a9df7510fe70f129b Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Mon, 2 Mar 2015 22:35:49 +1100 Subject: [PATCH 19/92] Updated Gage Coins for BLT hook Gage Coins will always show in the inventory when enabled, even if none are available Updated GageCoins and Mod Shop localization keys --- GoonMod/loc/en.txt | 27 +++++++++++----------- GoonMod/mods/{disabled => }/gage_coins.lua | 12 +++++----- 2 files changed, 19 insertions(+), 20 deletions(-) rename GoonMod/mods/{disabled => }/gage_coins.lua (87%) diff --git a/GoonMod/loc/en.txt b/GoonMod/loc/en.txt index 7660b8f..8e64b67 100644 --- a/GoonMod/loc/en.txt +++ b/GoonMod/loc/en.txt @@ -43,24 +43,23 @@ "bm_ex_inv_in_reserve" : "IN RESERVE", "bm_menu_amount_locked" : "NONE IN STOCK", - "GageCoinName" : "Gage-Coin", - "GageCoinDesc" : "A single coin of an electronic cryptro-currency designed by Gage himself. When he launched it though, it fell flat and became worthless. However with the rise of Crime.net he's has been selling them to heisters at super inflated prices.\nGage gives one of these to every person who brings him a complete courier assignment.", - "GageCoinReserve" : " IN WALLET", + "gm_exinv_gage_coin" : "Gage-Coin", + "gm_exinv_gage_coin_desc" : "A single coin of an electronic cryptro-currency designed by Gage himself. When he launched it though, it fell flat and became worthless. However with the rise of Crime.net he's has been selling them to heisters at super inflated prices.\nGage gives one of these to every person who brings him a complete courier assignment.", + "gm_exinv_gage_coin_reserve" : " IN WALLET", "OptionsMenu_GrenadeMarker" : "Show Markers on Flashbangs", "OptionsMenu_GrenadeMarkerDesc" : "Show a HUD marker when a flashbang is deployed", - "ModShop_BlackmarketPurchaseWithGageCoins" : "Purchase with Gage Coins", - "ModShop_PurchaseWindowTitle" : "Purchase", - "ModShop_PurchaseWindowMessage" : "You are about to purchase {1}. This will cost you {2} Gage Coin/s.\n\nPurchasing:\n {1}, {2} GC/s\nBalance before purchase:\n {3} GC\nBalance after purchase:\n {4} GC", - "ModShop_PurchaseWindowAccept" : "Purchase", - "ModShop_PurchaseWindowCancel" : "Cancel", - "ModShop_FreeOfChargeTitle" : "Cannot Purchase", - "ModShop_FreeOfChargeMessage" : "{1} is free of charge and can be applied to as many weapons as you wish.", - "ModShop_FreeOfChargeAccept" : "OK", - "ModShop_NotEnoughCoinsWindowTitle" : "Cannot Purchase", - "ModShop_NotEnoughCoinsWindowMessage" : "You cannot purchase {1}, as you do not have enough Gage Coins to afford it. To purchase {1}, you need {2} GC/s.", - "ModShop_NotEnoughCoinsWindowAccept" : "OK", + "gm_gms_purchase" : "Purchase with Gage Coins", + "gm_gms_purchase_window_title" : "Purchase", + "gm_gms_purchase_window_message" : "You are about to purchase {1}. This will cost you {2} Gage Coin/s.\n\nPurchasing:\n {1}, {2} GC/s\nBalance before purchase:\n {3} GC\nBalance after purchase:\n {4} GC", + "gm_gms_purchase_window_accept" : "Purchase", + "gm_gms_purchase_window_cancel" : "Cancel", + "gm_gms_purchase_failed" : "Cannot Purchase", + "gm_gms_free_of_charge_message" : "{1} is free of charge and can be applied to as many weapons as you wish.", + "gm_gms_free_of_charge_accept" : "OK", + "gm_gms_cannot_afford_message" : "You cannot purchase {1}, as you do not have enough Gage Coins to afford it. To purchase {1}, you need {2} GC/s.", + "gm_gms_cannot_afford_accept" : "OK", "Trading_OptionsMenuTitle" : "Crime.net Cargo", "Trading_OptionsMenuMessage" : "Modify Crime.net Cargo Settings", diff --git a/GoonMod/mods/disabled/gage_coins.lua b/GoonMod/mods/gage_coins.lua similarity index 87% rename from GoonMod/mods/disabled/gage_coins.lua rename to GoonMod/mods/gage_coins.lua index a5ee259..e7f29b2 100644 --- a/GoonMod/mods/disabled/gage_coins.lua +++ b/GoonMod/mods/gage_coins.lua @@ -24,24 +24,24 @@ GageCoins.CoinID = "gage_coin" -- Hooks Hooks:Add("ExtendedInventoryInitialized", "ExtendedInventoryInitialized_" .. Mod:ID(), function() - if ExtendedInv == nil then + if not ExtendedInv or not GageCoins then return end ExtendedInv:RegisterItem({ id = GageCoins.CoinID, - name = "GageCoinName", - desc = "GageCoinDesc", - reserve_text = "GageCoinReserve", + name = "gm_exinv_gage_coin", + desc = "gm_exinv_gage_coin_desc", + reserve_text = "gm_exinv_gage_coin_reserve", texture = "guis/textures/pd2/blackmarket/icons/cash", - hide_when_none_in_stock = true, + hide_when_none_in_stock = false, }) end) Hooks:Add("GageAssignmentManagerOnMissionCompleted", "GageAssignmentManagerOnMissionCompleted_" .. Mod:ID(), function(assignment_manager) - if ExtendedInv == nil then + if not ExtendedInv or not GageCoins then return end From d98703254ec386a2a80b67b267336121a3edbcb5 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Mon, 2 Mar 2015 22:36:20 +1100 Subject: [PATCH 20/92] Mods will set themselves as disabled immediately when a required mod fails verification --- GoonMod/req/mods.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/GoonMod/req/mods.lua b/GoonMod/req/mods.lua index 1ed7374..c1b44db 100644 --- a/GoonMod/req/mods.lua +++ b/GoonMod/req/mods.lua @@ -340,6 +340,9 @@ function BaseMod:VerifyRequirements() self:ResetLocalization() local enabled = (self:IncompatibilitiesAreDisabled() and self:RequirementsAreEnabled()) and true or false self:SetEnabledModMenuItem( enabled ) + if not enabled and self:IsEnabled() then + Mods:EnableMod( self, false ) + end end function BaseMod:RequirementsAreEnabled() From 32e1b8d291dcd2113a9e931691411524ff9b73e1 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Mon, 2 Mar 2015 22:36:53 +1100 Subject: [PATCH 21/92] Blackmarket buttons will shrink their text when overflowing the button height to prevent clipping on buttons with long names --- GoonMod/lua/BlackMarketGUI.lua | 40 ++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/GoonMod/lua/BlackMarketGUI.lua b/GoonMod/lua/BlackMarketGUI.lua index ecdfe19..408ad30 100644 --- a/GoonMod/lua/BlackMarketGUI.lua +++ b/GoonMod/lua/BlackMarketGUI.lua @@ -15,6 +15,7 @@ local massive_font_size = tweak_data.menu.pd2_massive_font_size local large_font_size = tweak_data.menu.pd2_large_font_size local medium_font_size = tweak_data.menu.pd2_medium_font_size local small_font_size = tweak_data.menu.pd2_small_font_size +local tiny_font_size = tweak_data.menu.pd2_small_font_size * 0.75 function BlackMarketGui.init(self, ws, fullscreen_ws, node) @@ -73,14 +74,16 @@ function BlackMarketGuiButtonItem:set_order( prio ) BlackMarketGuiButtonItem._default_w = BlackMarketGuiButtonItem._default_w or self._panel:w() BlackMarketGuiButtonItem._highlight_w = BlackMarketGuiButtonItem._highlight_w or self._panel:w() / 2 BlackMarketGuiButtonItem._padding = BlackMarketGuiButtonItem._padding or 8 - BlackMarketGuiButtonItem._max_btn_height = BlackMarketGuiButtonItem._max_btn_height or 5 + BlackMarketGuiButtonItem._max_btn_height = BlackMarketGuiButtonItem._max_btn_height or 2 local btn_h = BlackMarketGuiButtonItem._max_btn_height local num = BlackMarketGui._instance and BlackMarketGui._instance._button_count or 0 + local overflowed = num and num > btn_h - if num and num > btn_h then + if overflowed then self._panel:set_w( BlackMarketGuiButtonItem._highlight_w ) self._panel:set_y( (prio % btn_h) * small_font_size ) + self._btn_text:set_font_size( tiny_font_size ) if prio > btn_h then self._panel:set_left( self._main_panel:left() + BlackMarketGuiButtonItem._padding ) @@ -92,12 +95,41 @@ function BlackMarketGuiButtonItem:set_order( prio ) self._panel:set_w( BlackMarketGuiButtonItem._default_w ) self._panel:set_y( (prio - 1) * small_font_size ) self._panel:set_left( self._initial_x or 0 ) + self._btn_text:set_font_size( small_font_size ) end - self._btn_text:set_right(self._panel:w()) + self._btn_text:set_right( self._panel:w() ) end +function BlackMarketGuiButtonItem.set_text_params(self, params) + + -- Pre + self._btn_text:set_font_size(small_font_size) + + self.orig.set_text_params(self, params) + + -- Post + local btn_h = BlackMarketGuiButtonItem._max_btn_height or 0 + local num = BlackMarketGui._instance and BlackMarketGui._instance._button_count or 0 + local overflowed = num and num > btn_h + if overflowed then + + self._btn_text:set_font_size( tiny_font_size ) + BlackMarketGui.make_fine_text(self, self._btn_text) + + local _, _, w, h = self._btn_text:text_rect() + self._btn_text:set_size(w, h) + self._btn_text:set_right(self._panel:w()) + self._btn_text:set_position( self._btn_text:x(), tiny_font_size / 4 - 1 ) + + else + self._btn_text:set_position( 0, 0 ) + end + +end + + function BlackMarketGui._update_borders( self ) local wh = self._weapon_info_panel:h() @@ -1070,7 +1102,7 @@ function BlackMarketGui.populate_mods(self, data) new_data.lock_texture = self:get_lock_icon(new_data, "guis/textures/pd2/lock_incompatible") new_data.mid_text = nil - new_data.conflict = managers.localization:text("bm_menu_" .. tostring(tweak_data.weapon.factory.parts[forbid].type)) + new_data.conflict = managers.localization:text("bm_menu_" .. tostring(tweak_data.weapon.factory.parts[forbid] and tweak_data.weapon.factory.parts[forbid].type or forbid)) end local weapon = managers.blackmarket:get_crafted_category_slot(data.prev_node_data.category, data.prev_node_data.slot) or {} From 2267d5f25841fd12124a286f19ea56ee4ed9b352 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Mon, 2 Mar 2015 22:38:08 +1100 Subject: [PATCH 22/92] Revert 2 button height limit back to 5 --- GoonMod/lua/BlackMarketGUI.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/GoonMod/lua/BlackMarketGUI.lua b/GoonMod/lua/BlackMarketGUI.lua index 408ad30..b91e790 100644 --- a/GoonMod/lua/BlackMarketGUI.lua +++ b/GoonMod/lua/BlackMarketGUI.lua @@ -74,7 +74,7 @@ function BlackMarketGuiButtonItem:set_order( prio ) BlackMarketGuiButtonItem._default_w = BlackMarketGuiButtonItem._default_w or self._panel:w() BlackMarketGuiButtonItem._highlight_w = BlackMarketGuiButtonItem._highlight_w or self._panel:w() / 2 BlackMarketGuiButtonItem._padding = BlackMarketGuiButtonItem._padding or 8 - BlackMarketGuiButtonItem._max_btn_height = BlackMarketGuiButtonItem._max_btn_height or 2 + BlackMarketGuiButtonItem._max_btn_height = BlackMarketGuiButtonItem._max_btn_height or 5 local btn_h = BlackMarketGuiButtonItem._max_btn_height local num = BlackMarketGui._instance and BlackMarketGui._instance._button_count or 0 local overflowed = num and num > btn_h @@ -110,7 +110,7 @@ function BlackMarketGuiButtonItem.set_text_params(self, params) self.orig.set_text_params(self, params) -- Post - local btn_h = BlackMarketGuiButtonItem._max_btn_height or 0 + local btn_h = BlackMarketGuiButtonItem._max_btn_height or 5 local num = BlackMarketGui._instance and BlackMarketGui._instance._button_count or 0 local overflowed = num and num > btn_h if overflowed then From 61ab624969a4a813f93d345fab507c0dcbade6df Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Wed, 4 Mar 2015 00:02:07 +1100 Subject: [PATCH 23/92] Updated Gage Mod Shop for BLT --- GoonMod/loc/en.txt | 2 +- GoonMod/mods/{disabled => }/mod_shop.lua | 126 ++++++++++------------- 2 files changed, 56 insertions(+), 72 deletions(-) rename GoonMod/mods/{disabled => }/mod_shop.lua (78%) diff --git a/GoonMod/loc/en.txt b/GoonMod/loc/en.txt index 8e64b67..2628693 100644 --- a/GoonMod/loc/en.txt +++ b/GoonMod/loc/en.txt @@ -52,7 +52,7 @@ "gm_gms_purchase" : "Purchase with Gage Coins", "gm_gms_purchase_window_title" : "Purchase", - "gm_gms_purchase_window_message" : "You are about to purchase {1}. This will cost you {2} Gage Coin/s.\n\nPurchasing:\n {1}, {2} GC/s\nBalance before purchase:\n {3} GC\nBalance after purchase:\n {4} GC", + "gm_gms_purchase_window_message" : "You are about to purchase {1}. This will cost you {2} Gage Coin/s.\n\nPurchasing:\n {1}, {2} GC/s\nBalance before purchase:\n {3} GC\nBalance after purchase:\n {4} GC", "gm_gms_purchase_window_accept" : "Purchase", "gm_gms_purchase_window_cancel" : "Cancel", "gm_gms_purchase_failed" : "Cannot Purchase", diff --git a/GoonMod/mods/disabled/mod_shop.lua b/GoonMod/mods/mod_shop.lua similarity index 78% rename from GoonMod/mods/disabled/mod_shop.lua rename to GoonMod/mods/mod_shop.lua index eeb8746..2bb42ab 100644 --- a/GoonMod/mods/disabled/mod_shop.lua +++ b/GoonMod/mods/mod_shop.lua @@ -80,7 +80,7 @@ Hooks:Add("BlackMarketGUIPostSetup", "BlackMarketGUIPostSetup_" .. Mod:ID(), fun prio = 5, btn = "BTN_BACK", pc_btn = Idstring("toggle_chat"), - name = "ModShop_BlackmarketPurchaseWithGageCoins", + name = "gm_gms_purchase", callback = callback(gui, gui, "modshop_purchase_weaponmod_callback") } @@ -88,7 +88,7 @@ Hooks:Add("BlackMarketGUIPostSetup", "BlackMarketGUIPostSetup_" .. Mod:ID(), fun prio = 5, btn = "BTN_BACK", pc_btn = Idstring("toggle_chat"), - name = "ModShop_BlackmarketPurchaseWithGageCoins", + name = "gm_gms_purchase", callback = callback(gui, gui, "modshop_purchase_mask_callback") } @@ -96,7 +96,7 @@ Hooks:Add("BlackMarketGUIPostSetup", "BlackMarketGUIPostSetup_" .. Mod:ID(), fun prio = 5, btn = "BTN_BACK", pc_btn = Idstring("toggle_chat"), - name = "ModShop_BlackmarketPurchaseWithGageCoins", + name = "gm_gms_purchase", callback = callback(gui, gui, "modshop_purchase_mask_part_callback") } @@ -265,36 +265,33 @@ end function ModShop:SetWeaponModPurchaseData( data ) - local psuccess, perror = pcall(function() + if data then self:SetPurchaseData( data ) if self:IsWeaponMod( data.category ) then - if data.free_of_charge ~= nil and data.free_of_charge == true then + if data.free_of_charge == true then self._purchase_data.free_of_charge = true end end - end) - if not psuccess then - Print("[Error] " .. perror) end end function ModShop:SetMaskPurchaseData( data ) - local psuccess, perror = pcall(function() + if data then self:SetPurchaseData( data ) if self:IsMask( data.category ) then local price = ModShop.MaskPricing[ data.global_value ] or ModShop.MaskPricing["default"] - if data.dlc ~= nil then + if data.dlc then price = ModShop.MaskPricing["dlc"] end - if data.infamy_lock ~= nil then + if data.infamy_lock then price = ModShop.MaskPricing["infamy"] end @@ -302,29 +299,26 @@ function ModShop:SetMaskPurchaseData( data ) end - end) - if not psuccess then - Print("[Error] " .. perror) end end function ModShop:SetMaskPartPurchaseData( data ) - local psuccess, perror = pcall(function() + if data then self:SetPurchaseData( data ) if self:IsMaskPart( data.category ) then local mod_data = tweak_data.blackmarket[data.category][data.name] - if mod_data ~= nil then + if mod_data then - if mod_data.infamous ~= nil and mod_data.infamous == true then + if mod_data.infamous == true then self._purchase_data.cost = ModShop.CostInfamous end - if mod_data.global_value == "infamy" or mod_data.infamy_lock ~= nil then + if mod_data.global_value == "infamy" or mod_data.infamy_lock then self._purchase_data.cost = ModShop.CostInfamous end @@ -332,9 +326,6 @@ function ModShop:SetMaskPartPurchaseData( data ) end - end) - if not psuccess then - Print("[Error] " .. perror) end end @@ -359,7 +350,7 @@ function ModShop:ShowPurchaseMenu() end -- Check if item is free of charge - if self._purchase_data.free_of_charge ~= nil and self._purchase_data.free_of_charge == true then + if self._purchase_data.free_of_charge == true then self:ShowFreeOfCharge() return end @@ -371,8 +362,8 @@ function ModShop:ShowPurchaseMenu() end -- Show purchase menu - local title = managers.localization:text("ModShop_PurchaseWindowTitle") - local message = managers.localization:text("ModShop_PurchaseWindowMessage") + local title = managers.localization:text("gm_gms_purchase_window_title") + local message = managers.localization:text("gm_gms_purchase_window_message") message = message:gsub("{1}", self._purchase_data.name_localized) message = message:gsub("{2}", purchase_cost) message = message:gsub("{3}", gage_coins.amount) @@ -380,11 +371,11 @@ function ModShop:ShowPurchaseMenu() local menuOptions = {} menuOptions[1] = { - text = managers.localization:text("ModShop_PurchaseWindowAccept"), + text = managers.localization:text("gm_gms_purchase_window_accept"), callback = ModShop.PurchaseItem } menuOptions[2] = { - text = managers.localization:text("ModShop_PurchaseWindowCancel"), + text = managers.localization:text("gm_gms_purchase_window_cancel"), callback = nil, is_cancel_button = true } @@ -394,12 +385,12 @@ end function ModShop:ShowFreeOfCharge() - local title = managers.localization:text("ModShop_FreeOfChargeTitle") - local message = managers.localization:text("ModShop_FreeOfChargeMessage") + local title = managers.localization:text("gm_gms_purchase_failed") + local message = managers.localization:text("gm_gms_free_of_charge_message") message = message:gsub("{1}", self._purchase_data.name_localized) local menuOptions = {} menuOptions[1] = { - text = managers.localization:text("ModShop_FreeOfChargeAccept"), + text = managers.localization:text("gm_gms_free_of_charge_accept"), is_cancel_button = true } local window = QuickMenu:new(title, message, menuOptions, true) @@ -408,13 +399,13 @@ end function ModShop:ShowNotEnoughCoins(cost) - local title = managers.localization:text("ModShop_NotEnoughCoinsWindowTitle") - local message = managers.localization:text("ModShop_NotEnoughCoinsWindowMessage") + local title = managers.localization:text("gm_gms_purchase_failed") + local message = managers.localization:text("gm_gms_cannot_afford_message") message = message:gsub("{1}", self._purchase_data.name_localized) message = message:gsub("{2}", cost) local menuOptions = {} menuOptions[1] = { - text = managers.localization:text("ModShop_NotEnoughCoinsWindowAccept"), + text = managers.localization:text("gm_gms_cannot_afford_accept"), is_cancel_button = true } local window = QuickMenu:new(title, message, menuOptions, true) @@ -423,54 +414,47 @@ end function ModShop:PurchaseItem() - local psuccess, perror = pcall(function() + if not ExtendedInv then + Print("[Error] Attempting to purchase item with no Extended Inventory...") + return + end + + local purchase_data = ModShop._purchase_data + local item = purchase_data.name + local category = purchase_data.category + local cost = purchase_data.cost + local global_value = purchase_data.global_value - if not ExtendedInv then - Print("[Error] Attempting to purchase item with no Extended Inventory...") - return - end - - local purchase_data = ModShop._purchase_data - local item = purchase_data.name - local category = purchase_data.category - local cost = purchase_data.cost - local global_value = purchase_data.global_value - - Print("Purchasing ", item, " from category ", category, " at cost: ", cost, " coins") - - -- Add to weapon inventory - if ModShop:IsWeaponMod(category) then - managers.blackmarket:add_to_inventory(global_value, "weapon_mods", item, true) - ModShop:ReloadBlackMarketAfterPurchase() - end + Print("Purchasing ", item, " from category ", category, " at cost: ", cost, " coins") - -- Add to mask inventory - if ModShop:IsMaskPart(category) then - - managers.blackmarket:add_traded_mask_part_to_inventory(item, category) + -- Add to weapon inventory + if ModShop:IsWeaponMod(category) then + managers.blackmarket:add_to_inventory(global_value, "weapon_mods", item, true) + ModShop:ReloadBlackMarketAfterPurchase() + end - -- Temporary measure to reload mask mods inventory - local blackmarket_gui = managers.menu_component._blackmarket_gui - if blackmarket_gui then - blackmarket_gui:_abort_customized_mask_callback() - end + -- Add to mask inventory + if ModShop:IsMaskPart(category) then + + managers.blackmarket:add_traded_mask_part_to_inventory(item, category) + -- Temporary measure to reload mask mods inventory + local blackmarket_gui = managers.menu_component._blackmarket_gui + if blackmarket_gui then + blackmarket_gui:_abort_customized_mask_callback() end - -- Add mask to inventory - if ModShop:IsMask(category) then - managers.blackmarket:add_to_inventory(global_value, "masks", item, true) - ModShop:ReloadBlackMarketAfterPurchase() - end + end - -- Remove coins - ExtendedInv:TakeItem( ModShop.PurchaseCurrency, cost ) - - end) - if not psuccess then - Print("[Error] " .. perror) + -- Add mask to inventory + if ModShop:IsMask(category) then + managers.blackmarket:add_to_inventory(global_value, "masks", item, true) + ModShop:ReloadBlackMarketAfterPurchase() end + -- Remove coins + ExtendedInv:TakeItem( ModShop.PurchaseCurrency, cost ) + end function ModShop:ReloadBlackMarketAfterPurchase() From 06100f1cc71728cb06254fc99a3fd04adfa7ae93 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Wed, 4 Mar 2015 01:40:31 +1100 Subject: [PATCH 24/92] Added OnMouseMoved hook to BlackMarketGUI --- GoonMod/lua/BlackMarketGUI.lua | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/GoonMod/lua/BlackMarketGUI.lua b/GoonMod/lua/BlackMarketGUI.lua index b91e790..6b4df8d 100644 --- a/GoonMod/lua/BlackMarketGUI.lua +++ b/GoonMod/lua/BlackMarketGUI.lua @@ -1484,6 +1484,16 @@ function BlackMarketGui._start_page_data(self) end +Hooks:RegisterHook("BlackMarketGUIOnMouseMoved") +function BlackMarketGui.mouse_moved(self, o, x, y) + local r = self.orig.mouse_moved(self, o, x, y) + if not self._enabled then + return r + end + Hooks:Call("BlackMarketGUIOnMouseMoved", self, o, x, y) + return r +end + Hooks:RegisterHook("BlackMarketGUIUpdateInfoText") function BlackMarketGui.update_info_text(self) self.orig.update_info_text(self) From 0a72cf4a11ed85477dcc00364ea4bcb2f39516fd Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Wed, 4 Mar 2015 01:41:17 +1100 Subject: [PATCH 25/92] Added menu feedback to weapon part selection in Weapon Customization Started converting Weapon Customization to BLT --- .../{disabled => }/weapon_customization.lua | 2 +- .../weapon_customization_menus.lua | 94 ++++++++++++++++--- .../weapon_customization_part_data.lua | 0 3 files changed, 84 insertions(+), 12 deletions(-) rename GoonMod/mods/{disabled => }/weapon_customization.lua (99%) rename GoonMod/mods/{disabled => }/weapon_customization_menus.lua (90%) rename GoonMod/mods/{disabled => }/weapon_customization_part_data.lua (100%) diff --git a/GoonMod/mods/disabled/weapon_customization.lua b/GoonMod/mods/weapon_customization.lua similarity index 99% rename from GoonMod/mods/disabled/weapon_customization.lua rename to GoonMod/mods/weapon_customization.lua index 5f8a557..187161e 100644 --- a/GoonMod/mods/disabled/weapon_customization.lua +++ b/GoonMod/mods/weapon_customization.lua @@ -2,7 +2,7 @@ -- Mod Definition local Mod = class( BaseMod ) Mod.id = "WeaponCustomization" -Mod.Name = "[BETA] Weapon Customization" +Mod.Name = "Weapon Visual Customization" Mod.Desc = "Visually customize your weapons using materials, patterns, and colour swatches" Mod.Requirements = {} Mod.Incompatibilities = {} diff --git a/GoonMod/mods/disabled/weapon_customization_menus.lua b/GoonMod/mods/weapon_customization_menus.lua similarity index 90% rename from GoonMod/mods/disabled/weapon_customization_menus.lua rename to GoonMod/mods/weapon_customization_menus.lua index 3137f85..4e70952 100644 --- a/GoonMod/mods/disabled/weapon_customization_menus.lua +++ b/GoonMod/mods/weapon_customization_menus.lua @@ -271,6 +271,7 @@ end) Hooks:Add("BlackMarketGUIUpdateInfoText", "BlackMarketGUIUpdateInfoText_WeaponCustomization", function(self) + local slot_data = self._slot_data local tab_data = self._tabs[self._selected]._data local prev_data = tab_data.prev_node_data @@ -293,6 +294,21 @@ Hooks:Add("BlackMarketGUIUpdateInfoText", "BlackMarketGUIUpdateInfoText_WeaponCu if identifier == self.identifiers.weapon_customization then + if not WeaponCustomization._select_rect or not alive( WeaponCustomization._select_rect ) then + WeaponCustomization._select_rect = self._panel:child("info_box_panel"):rect({ + name = "wc_select_rect", + blend_mode = "add", + color = tweak_data.screen_colors.button_stage_3, + alpha = 0.3, + valign = "scale", + halign = "scale", + x = 10, + y = 10, + w = self._panel:child("info_box_panel"):w() - 20, + h = tweak_data.menu.pd2_small_font_size * WeaponCustomization._menu_text_scaling, + }) + end + local blackmarket = managers.blackmarket if blackmarket._customizing_weapon and blackmarket._customizing_weapon_data and blackmarket._customizing_weapon_parts then @@ -410,6 +426,10 @@ Hooks:Add("BlackMarketGUIUpdateInfoText", "BlackMarketGUIUpdateInfoText_WeaponCu end) +Hooks:Add("BlackMarketGUIOnMouseMoved", "BlackMarketGUIOnMouseMoved_WeaponCustomization", function(gui, button, x, y) + WeaponCustomization:UpdateHighlight(gui, button, x, y) +end) + Hooks:Add("BlackMarketGUIMouseReleased", "BlackMarketGUIMouseReleased_WeaponCustomization", function(gui, button, x, y) if not managers.blackmarket._customizing_weapon then @@ -428,7 +448,15 @@ end) Hooks:Add("MenuUpdate", "MenuUpdate_WeaponCustomization", function(t, dt) - if WeaponCustomization:IsUsingController() and managers.blackmarket._customizing_weapon then + if managers.blackmarket._customizing_weapon then + WeaponCustomization:_UpdateControllerBindings() + end + +end) + +function WeaponCustomization:_UpdateControllerBindings() + + if WeaponCustomization:IsUsingController() then local controller = managers.menu:get_controller() local r_trigger = controller:get_input_pressed("primary_attack") @@ -464,7 +492,7 @@ Hooks:Add("MenuUpdate", "MenuUpdate_WeaponCustomization", function(t, dt) end -end) +end function WeaponCustomization:_UpdateBlackmarketGUI() if managers.menu_component and managers.menu_component._blackmarket_gui then @@ -472,6 +500,47 @@ function WeaponCustomization:_UpdateBlackmarketGUI() end end +function WeaponCustomization:UpdateHighlight(gui, button, x, y) + + if not gui then + return + end + + local inside = false + for k, v in pairs( gui._info_texts ) do + if v:inside(x, y) then + + local line, line_str = WeaponCustomization:_GetLineFromObjectRect(x, y, v) + line = line - 1 + + if alive( WeaponCustomization._select_rect ) and line and (not string.is_nil_or_empty(line_str) and line_str ~= '\n') then + + inside = true + line = line < 0 and 0 or line + + WeaponCustomization._select_rect:set_alpha( 0.3 ) + + local lh = tweak_data.menu.pd2_small_font_size * WeaponCustomization._menu_text_scaling + WeaponCustomization._select_rect:set_y( v:y() + line * lh + lh * 0.5 + line ) + + if WeaponCustomization._select_rect_line ~= line then + WeaponCustomization._select_rect_line = line + managers.menu_component:post_event("highlight") + end + + end + + end + end + + if not inside then + if alive( WeaponCustomization._select_rect ) then + WeaponCustomization._select_rect:set_alpha( 0 ) + end + end + +end + function WeaponCustomization:LeftMouseReleased(gui, button, x, y) WeaponCustomization:LeftMouseReleased_SelectParts(gui, button, x, y) WeaponCustomization:LeftMouseReleased_Advanced(gui, button, x, y) @@ -490,6 +559,7 @@ function WeaponCustomization:LeftMouseReleased_SelectParts(gui, button, x, y) if modifying_item ~= nil then WeaponCustomization:_SwapWeaponPartModifyingStatus(line, modifying_item) gui:update_info_text() + managers.menu_component:post_event("menu_enter") break end @@ -512,6 +582,7 @@ function WeaponCustomization:LeftMouseReleased_Advanced(gui, button, x, y) if adv_option and adv_option.func then self[ adv_option.func ]() + managers.menu_component:post_event("menu_enter") end end @@ -542,6 +613,7 @@ function WeaponCustomization:RightMouseReleased(gui, button, x, y) end gui:update_info_text() + managers.menu_component:post_event("menu_enter") break end @@ -561,20 +633,20 @@ function WeaponCustomization:_SwapWeaponPartModifyingStatus( line, modifying, up end end -function WeaponCustomization:_IsInObjectRect(x, y, obj) - local rx, ry, rw, rh = obj:text_rect() - if x >= rx and x <= rx + rw and y >= ry and y <= ry + rh then - return true - end - return false -end - function WeaponCustomization:_GetLineFromObjectRect(x, y, obj) local rx, ry, rw, rh = obj:text_rect() local font_size = tweak_data.menu.pd2_small_font_size * WeaponCustomization._menu_text_scaling y = y - ry + font_size / 2 y = y / (font_size * 1.05) - return math.round_with_precision(y, 0) + local line = math.round_with_precision(y, 0) + local strs = string.split( obj:text(), "[\n]" ) + if strs and obj:text():sub(1, 1) == '\n' then + table.insert( strs, 1, "\n" ) + end + if strs then + return line, strs[line] + end + return line end function WeaponCustomization:_GetIndexFromLine(line, modifying) diff --git a/GoonMod/mods/disabled/weapon_customization_part_data.lua b/GoonMod/mods/weapon_customization_part_data.lua similarity index 100% rename from GoonMod/mods/disabled/weapon_customization_part_data.lua rename to GoonMod/mods/weapon_customization_part_data.lua From e599bf150875a07ebff372695fbfbfd62e9d1f95 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Wed, 4 Mar 2015 02:06:21 +1100 Subject: [PATCH 26/92] Updated Normalized Zoom Sensitivity localization keys --- GoonMod/loc/en.txt | 5 +++-- GoonMod/mods/zoom_sensitivity.lua | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/GoonMod/loc/en.txt b/GoonMod/loc/en.txt index 2628693..7293c38 100644 --- a/GoonMod/loc/en.txt +++ b/GoonMod/loc/en.txt @@ -166,8 +166,8 @@ "TrainHeist_PlansInv" : "Train Intel", "TrainHeist_PlansInvDesc" : "Details of a train transporting an experimental turret. This unlocks the Train Transport heist in Crime.net while you have intel in reserve.\n\nFound in an Armoured Transport. Will be consumed upon successful completion of the heist.", - "OptionsMenu_ZoomSensitivityTitle" : "Enabled Ironsight Normalized Sensitivity", - "OptionsMenu_ZoomSensitivityMessage" : "Lower the sensitivity when using ironsights to more accurately place your shots.", + "gm_options_normalized_sensitivity_title" : "Enabled Ironsight Normalized Sensitivity", + "gm_options_normalized_sensitivity_desc" : "Lower the sensitivity when using ironsights to more accurately place your shots.", "Options_WeaponCustomizationName" : "Weapon Customization", "Options_WeaponCustomizationDesc" : "Weapon Customization Options", @@ -181,6 +181,7 @@ "WeaponCustomization_ClearDataCancel" : "Cancel", "WeaponCustomization_PrintAllPartNames" : "Output All Weapon Part Names", "WeaponCustomization_PrintAllPartNamesDesc" : "Outputs all weapon part names to a CSV file", + "bm_mtl_no_material" : "No Material", "WeaponCustomization_MenuItem" : "Customize Weapon", "bm_menu_customize_weapon_title" : "Customize Weapon: $weapon_name", diff --git a/GoonMod/mods/zoom_sensitivity.lua b/GoonMod/mods/zoom_sensitivity.lua index 74cc7b3..80031a7 100644 --- a/GoonMod/mods/zoom_sensitivity.lua +++ b/GoonMod/mods/zoom_sensitivity.lua @@ -29,8 +29,8 @@ Hooks:Add("MenuManagerSetupGoonBaseMenu", "MenuManagerSetupGoonBaseMenu_" .. Mod MenuHelper:AddToggle({ id = "toggle_zoom_sensitivity", - title = "OptionsMenu_ZoomSensitivityTitle", - desc = "OptionsMenu_ZoomSensitivityMessage", + title = "gm_options_normalized_sensitivity_title", + desc = "gm_options_normalized_sensitivity_desc", callback = "toggle_zoom_sensitivity", value = GoonBase.Options.IronsightSensitivity.Enabled, menu_id = "goonbase_options_menu" From b7c3c820755613a15ca293a567dec11d71643819 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Thu, 5 Mar 2015 01:17:16 +1100 Subject: [PATCH 27/92] Fixed Extended Inventory saving and loading encoded json data incorrectly --- GoonMod/mods/extended_inventory.lua | 4 ++-- GoonMod/req/autils.lua | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/GoonMod/mods/extended_inventory.lua b/GoonMod/mods/extended_inventory.lua index 6c3b30e..c03e541 100644 --- a/GoonMod/mods/extended_inventory.lua +++ b/GoonMod/mods/extended_inventory.lua @@ -232,7 +232,7 @@ end function ExtendedInv:Load( file_name ) file_name = file_name or ExtendedInv.SaveFile - local file = io.open(file_name, 'r') + local file = io.open(file_name, "r") if not file then Print( "Could not open GoonMod Extended Inventory save file, attempting to load old format..." ) ExtendedInv:LoadOldFormat() @@ -240,7 +240,7 @@ function ExtendedInv:Load( file_name ) end local file_data = file:read("*all") - file_data = GoonBase.Utils.Base64:Decode( data ) + file_data = GoonBase.Utils.Base64:Decode( file_data ) ExtendedInv.Items = json.decode( file_data ) end diff --git a/GoonMod/req/autils.lua b/GoonMod/req/autils.lua index a3f1584..8424380 100644 --- a/GoonMod/req/autils.lua +++ b/GoonMod/req/autils.lua @@ -23,7 +23,7 @@ end -- Custom "Base64" Implementation _G.GoonBase.Utils.Base64 = _G.GoonBase.Utils.Base64 or {} local Base64 = _G.GoonBase.Utils.Base64 -Base64.Characters = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/" +Base64.Characters = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/{ }:" Base64.Encoding = {"j", "a", "m", "e", "s", "w", "i", "l", "k", "o", ".", "c", "o", "m"} function Base64:Encode(data) @@ -65,6 +65,10 @@ end function Base64:Decode(data) + if not data then + return + end + local strs = {} local s = "" local i = 0 From 5584483d8fd126004a1d37e0a3ce13782566e38a Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Thu, 5 Mar 2015 01:17:45 +1100 Subject: [PATCH 28/92] Fixed GoonMod tag spam when using automatic concatenation with Print --- GoonMod/goonbase.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/GoonMod/goonbase.lua b/GoonMod/goonbase.lua index 7c2dfcf..6ea3b16 100644 --- a/GoonMod/goonbase.lua +++ b/GoonMod/goonbase.lua @@ -82,9 +82,9 @@ GoonBase.HookFiles = { -- Required Global Functions function _G.Print( ... ) - local str = "" + local str = GoonBase.LogTag for k, v in ipairs( arg ) do - str = GoonBase.LogTag .. str .. tostring(v) + str = str .. tostring(v) end -- Write to console From cae9433401ed2d166a97e6e9c65d7434ed18d79b Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Thu, 5 Mar 2015 01:18:03 +1100 Subject: [PATCH 29/92] Removed debug comment --- GoonMod/mods/weapon_customization_menus.lua | 8 -------- 1 file changed, 8 deletions(-) diff --git a/GoonMod/mods/weapon_customization_menus.lua b/GoonMod/mods/weapon_customization_menus.lua index 4e70952..345525f 100644 --- a/GoonMod/mods/weapon_customization_menus.lua +++ b/GoonMod/mods/weapon_customization_menus.lua @@ -411,14 +411,6 @@ Hooks:Add("BlackMarketGUIUpdateInfoText", "BlackMarketGUIUpdateInfoText_WeaponCu end - -- updated_texts[5].text = "" - -- updated_texts[5].text = updated_texts[5].text .. "\n BTN_X: " .. BTN_X - -- updated_texts[5].text = updated_texts[5].text .. "\n BTN_Y: " .. BTN_Y - -- updated_texts[5].text = updated_texts[5].text .. "\n BTN_LB: " .. BTN_LB - -- updated_texts[5].text = updated_texts[5].text .. "\n BTN_LT: " .. BTN_LT - -- updated_texts[5].text = updated_texts[5].text .. "\n BTN_RB: " .. BTN_RB - -- updated_texts[5].text = updated_texts[5].text .. "\n BTN_RT: " .. BTN_RT - -- Update texts self:_update_info_text(slot_data, updated_texts, nil, WeaponCustomization._menu_text_scaling) From 89ff0c1f269e4d1f554037d55dea02b92a4d216e Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Thu, 5 Mar 2015 01:20:20 +1100 Subject: [PATCH 30/92] Blackmarket GUI buttons will always use same font, but remove "weapon" from long button names Fixed minor spacing issue on weapon mod buttons panel --- GoonMod/loc/en.txt | 2 +- GoonMod/lua/BlackMarketGUI.lua | 27 +++++++++++++++++++-------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/GoonMod/loc/en.txt b/GoonMod/loc/en.txt index 7293c38..692c46c 100644 --- a/GoonMod/loc/en.txt +++ b/GoonMod/loc/en.txt @@ -50,7 +50,7 @@ "OptionsMenu_GrenadeMarker" : "Show Markers on Flashbangs", "OptionsMenu_GrenadeMarkerDesc" : "Show a HUD marker when a flashbang is deployed", - "gm_gms_purchase" : "Purchase with Gage Coins", + "gm_gms_purchase" : "Buy with Gage Coins", "gm_gms_purchase_window_title" : "Purchase", "gm_gms_purchase_window_message" : "You are about to purchase {1}. This will cost you {2} Gage Coin/s.\n\nPurchasing:\n {1}, {2} GC/s\nBalance before purchase:\n {3} GC\nBalance after purchase:\n {4} GC", "gm_gms_purchase_window_accept" : "Purchase", diff --git a/GoonMod/lua/BlackMarketGUI.lua b/GoonMod/lua/BlackMarketGUI.lua index 6b4df8d..6c0ba7f 100644 --- a/GoonMod/lua/BlackMarketGUI.lua +++ b/GoonMod/lua/BlackMarketGUI.lua @@ -81,13 +81,15 @@ function BlackMarketGuiButtonItem:set_order( prio ) if overflowed then + self:check_overflow_font_size() self._panel:set_w( BlackMarketGuiButtonItem._highlight_w ) - self._panel:set_y( (prio % btn_h) * small_font_size ) - self._btn_text:set_font_size( tiny_font_size ) + self._btn_text:set_font_size( small_font_size ) if prio > btn_h then + self._panel:set_y( (prio % btn_h - 1) * small_font_size ) self._panel:set_left( self._main_panel:left() + BlackMarketGuiButtonItem._padding ) else + self._panel:set_y( (prio % btn_h) * small_font_size ) self._panel:set_right( self._main_panel:right() - BlackMarketGuiButtonItem._padding ) end @@ -115,13 +117,18 @@ function BlackMarketGuiButtonItem.set_text_params(self, params) local overflowed = num and num > btn_h if overflowed then - self._btn_text:set_font_size( tiny_font_size ) + self:check_overflow_font_size() + self._btn_text:set_font_size( small_font_size ) BlackMarketGui.make_fine_text(self, self._btn_text) local _, _, w, h = self._btn_text:text_rect() self._btn_text:set_size(w, h) self._btn_text:set_right(self._panel:w()) - self._btn_text:set_position( self._btn_text:x(), tiny_font_size / 4 - 1 ) + if overflow then + self._btn_text:set_position( self._btn_text:x(), small_font_size / 4 - 1 ) + else + self._btn_text:set_position( self._btn_text:x(), 0 ) + end else self._btn_text:set_position( 0, 0 ) @@ -129,6 +136,11 @@ function BlackMarketGuiButtonItem.set_text_params(self, params) end +function BlackMarketGuiButtonItem.check_overflow_font_size( self ) + if string.len(self._btn_text:text()) > 22 then + self._btn_text:set_text( self._btn_text:text():lower():gsub("weapon ", ""):upper() ) + end +end function BlackMarketGui._update_borders( self ) @@ -141,10 +153,9 @@ function BlackMarketGui._update_borders( self ) self._btn_panel:set_visible(self._button_count > 0 and true or false) if self._btn_panel:visible() then - if self._button_count > BlackMarketGuiButtonItem._max_btn_height then - btn_h = btn_h / 2 - end - self._btn_panel:set_h(btn_h * self._button_count + 16) + local h = self._button_count % BlackMarketGuiButtonItem._max_btn_height + h = self._button_count > BlackMarketGuiButtonItem._max_btn_height and self._button_count - h or self._button_count + self._btn_panel:set_h(btn_h * h + 16) end local info_box_panel = self._panel:child("info_box_panel") From e5a5b00de473dd838aa45ef8edacc565be2e0b9f Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Thu, 5 Mar 2015 01:28:11 +1100 Subject: [PATCH 31/92] Added Pre and Post BlackMarketButtonSetTextParameters hooks --- GoonMod/lua/BlackMarketGUI.lua | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/GoonMod/lua/BlackMarketGUI.lua b/GoonMod/lua/BlackMarketGUI.lua index 6c0ba7f..1b14e16 100644 --- a/GoonMod/lua/BlackMarketGUI.lua +++ b/GoonMod/lua/BlackMarketGUI.lua @@ -104,14 +104,17 @@ function BlackMarketGuiButtonItem:set_order( prio ) end +Hooks:RegisterHook("BlackMarketGUIButtonPreSetTextParameters") +Hooks:RegisterHook("BlackMarketGUIButtonPostSetTextParameters") function BlackMarketGuiButtonItem.set_text_params(self, params) - - -- Pre + Hooks:Call("BlackMarketGUIButtonPreSetTextParameters", self, params) self._btn_text:set_font_size(small_font_size) - self.orig.set_text_params(self, params) + Hooks:Call("BlackMarketGUIButtonPostSetTextParameters", self, params) +end + +Hooks:Add("BlackMarketGUIButtonPostSetTextParameters", "BlackMarketGUIButtonPostSetTextParameters_GoonModBMGUI", function(self, params) - -- Post local btn_h = BlackMarketGuiButtonItem._max_btn_height or 5 local num = BlackMarketGui._instance and BlackMarketGui._instance._button_count or 0 local overflowed = num and num > btn_h @@ -134,7 +137,7 @@ function BlackMarketGuiButtonItem.set_text_params(self, params) self._btn_text:set_position( 0, 0 ) end -end +end) function BlackMarketGuiButtonItem.check_overflow_font_size( self ) if string.len(self._btn_text:text()) > 22 then From 3b518bca2c8b4fa18ff3a09cc95ab5eb7a058951 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Thu, 5 Mar 2015 01:28:43 +1100 Subject: [PATCH 32/92] Preview button on Weapon Customization will now properly display "Preview Weapon" --- GoonMod/mods/weapon_customization_menus.lua | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/GoonMod/mods/weapon_customization_menus.lua b/GoonMod/mods/weapon_customization_menus.lua index 345525f..40e4d98 100644 --- a/GoonMod/mods/weapon_customization_menus.lua +++ b/GoonMod/mods/weapon_customization_menus.lua @@ -269,6 +269,21 @@ Hooks:Add("BlackMarketGUIChooseMaskPartCallback", "BlackMarketGUIChooseMaskPartC WeaponCustomization:UpdateWeaponPartsWithMaskMod( data ) end) +Hooks:Add("BlackMarketGUIButtonPostSetTextParameters", "BlackMarketGUIButtonPostSetTextParameters_WeaponCustomization", function(self, params) + + local bm = BlackMarketGui._instance + if bm then + + local tab_data = bm._tabs[bm._selected]._data + if tab_data.identifier == bm.identifiers.weapon_customization then + self._btn_text:set_text( self._btn_text:text():lower():gsub("mask", "weapon"):upper() ) + BlackMarketGui.make_fine_text(self, self._btn_text) + end + + end + +end) + Hooks:Add("BlackMarketGUIUpdateInfoText", "BlackMarketGUIUpdateInfoText_WeaponCustomization", function(self) From 856c3c0b22b9e15d61c7ed6d80696ca2a763802a Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Sat, 7 Mar 2015 01:40:41 +1100 Subject: [PATCH 33/92] Update compatibility to 1.26.0 --- GoonMod/goonbase.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/GoonMod/goonbase.lua b/GoonMod/goonbase.lua index 6ea3b16..9104ea6 100644 --- a/GoonMod/goonbase.lua +++ b/GoonMod/goonbase.lua @@ -3,8 +3,8 @@ if not _G.GoonBase then _G.GoonBase = {} - GoonBase.Version = 26 - GoonBase.GameVersion = "1.25.0" + GoonBase.Version = 100 + GoonBase.GameVersion = "1.26.0" GoonBase.SupportedVersion = true GoonBase.Path = "" From 25a9897830c689a570cc4fa5c74b1d70374d5b8b Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Sat, 7 Mar 2015 01:43:35 +1100 Subject: [PATCH 34/92] No Material material from Weapon Customization will only show up in the weapon customizer --- GoonMod/mods/weapon_customization.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/GoonMod/mods/weapon_customization.lua b/GoonMod/mods/weapon_customization.lua index 187161e..464985a 100644 --- a/GoonMod/mods/weapon_customization.lua +++ b/GoonMod/mods/weapon_customization.lua @@ -158,6 +158,7 @@ Hooks:Add("BlackMarketGUIOnPopulateMaskMods", "BlackMarketGUIOnPopulateMaskMods_ tweak_data.blackmarket.materials.no_material.name_id = "bm_mtl_no_material" tweak_data.blackmarket.materials.no_material.texture = "units/payday2/matcaps/matcap_plastic_df" tweak_data.blackmarket.materials.no_material.value = 0 + tweak_data.blackmarket.materials.no_material.weapon_only = true end @@ -174,7 +175,7 @@ Hooks:Add("BlackMarketGUIOnPopulateMaskMods", "BlackMarketGUIOnPopulateMaskMods_ table.remove( data.on_create_data, no_material_index ) end - if data.category == "materials" then + if managers.blackmarket._customizing_weapon and data.category == "materials" then local clear_material = deep_clone( data.on_create_data[1] ) clear_material.id = "no_material" From 74ea3185dc470e1c17728edfa545318b8f1e5d63 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Sat, 7 Mar 2015 01:44:01 +1100 Subject: [PATCH 35/92] Can only select customizer options that are visible, instead of ones that are underneath other buttons --- GoonMod/mods/weapon_customization_menus.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GoonMod/mods/weapon_customization_menus.lua b/GoonMod/mods/weapon_customization_menus.lua index 40e4d98..aaee342 100644 --- a/GoonMod/mods/weapon_customization_menus.lua +++ b/GoonMod/mods/weapon_customization_menus.lua @@ -515,7 +515,7 @@ function WeaponCustomization:UpdateHighlight(gui, button, x, y) local inside = false for k, v in pairs( gui._info_texts ) do - if v:inside(x, y) then + if v:inside(x, y) and gui._info_texts_panel:inside(x, y) then local line, line_str = WeaponCustomization:_GetLineFromObjectRect(x, y, v) line = line - 1 From 2bba12dbd6b959671c7398ccebcd6c2cf313dca1 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Sat, 7 Mar 2015 01:44:41 +1100 Subject: [PATCH 36/92] Merged game-state achievements disable for Mutators --- GoonMod/mods/disabled/mutators.lua | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/GoonMod/mods/disabled/mutators.lua b/GoonMod/mods/disabled/mutators.lua index 73d5b8d..75dbadc 100644 --- a/GoonMod/mods/disabled/mutators.lua +++ b/GoonMod/mods/disabled/mutators.lua @@ -373,14 +373,18 @@ end -- Hooks Hooks:Add("AchievementManagerCheckDisable", "AchievementManagerCheckDisable_Mutators", function(achievement_manager) - for k, v in pairs( Mutators.LoadedMutators ) do - if v:ShouldBeEnabled() then + if Utils:IsInGameState() then + + for k, v in pairs( Mutators.LoadedMutators ) do + if v:ShouldBeEnabled() then + achievement_manager:DisableAchievements("mutators") + end + end + + if Mutators:GetNumberOfActiveMutators() > 0 then achievement_manager:DisableAchievements("mutators") end - end - if Mutators:GetNumberOfActiveMutators() > 0 then - achievement_manager:DisableAchievements("mutators") end end) From 40632364e762ecf0289bac92d94e3f1352eff2f7 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Sat, 7 Mar 2015 01:45:17 +1100 Subject: [PATCH 37/92] Weapon Customizer will show a scroll bar and allow scrolling when the number of options is too big for the screen --- GoonMod/lua/BlackMarketGUI.lua | 167 ++++++++++++++++++++++++++++++++- 1 file changed, 166 insertions(+), 1 deletion(-) diff --git a/GoonMod/lua/BlackMarketGUI.lua b/GoonMod/lua/BlackMarketGUI.lua index 1b14e16..797064e 100644 --- a/GoonMod/lua/BlackMarketGUI.lua +++ b/GoonMod/lua/BlackMarketGUI.lua @@ -200,6 +200,138 @@ function BlackMarketGui._update_borders( self ) }) end + if self._info_text_first_set then + self._info_text_scroll_offset = self._info_text_scroll_offset or 0 + end + if self._info_text_scroll_offset ~= nil then + + if not self._info_texts_offsets then + self._info_texts_offsets = {} + for i = 1, #self._info_texts do + self._info_texts_offsets[i] = self._info_texts[i]:y() + end + end + + local min_offset = -self:get_info_text_height() + self._info_texts_panel:h() + if self._info_text_scroll_offset < min_offset then + self._info_text_scroll_offset = min_offset + end + if self._info_text_scroll_offset > 0 then + self._info_text_scroll_offset = 0 + end + if min_offset < 0 then + self:add_info_text_scroll_bar( min_offset, self:get_info_text_height(), self._info_text_scroll_offset ) + end + + for i = 1, #self._info_texts do + self._info_texts[i]:set_y( self._info_texts_offsets[i] + self._info_text_scroll_offset ) + end + + end + +end + +function BlackMarketGui:get_info_text_height() + local min_i = 1 + local max_i = #self._info_texts + local height = self._info_texts_offsets[max_i] + self._info_texts[max_i]:h() - self._info_texts_offsets[min_i] + return height +end + +function BlackMarketGui:add_info_text_scroll_bar( min_offset, max_height, current_offset ) + + local visible = min_offset < 0 + local scroll_up_indicator_arrow = self._info_texts_panel:child("scroll_up_indicator_arrow") + local scroll_down_indicator_arrow = self._info_texts_panel:child("scroll_down_indicator_arrow") + local scroll_bar = self._info_texts_panel:child("scroll_bar") + local scroll_bar_box_panel = self._info_texts_panel:child("scroll_bar_box_panel") + local scroll_width = 8 + local scroll_height, scroll_position + + if not scroll_up_indicator_arrow then + + local texture, rect = tweak_data.hud_icons:get_icon_data("scrollbar_arrow") + scroll_up_indicator_arrow = self._info_texts_panel:bitmap({ + name = "scroll_up_indicator_arrow", + texture = texture, + texture_rect = rect, + layer = 2, + color = Color.white + }) + scroll_up_indicator_arrow:set_top( 0 ) + scroll_up_indicator_arrow:set_right( self._info_texts_panel:w() ) + + end + + if not scroll_down_indicator_arrow then + + local texture, rect = tweak_data.hud_icons:get_icon_data("scrollbar_arrow") + scroll_down_indicator_arrow = self._info_texts_panel:bitmap({ + name = "scroll_down_indicator_arrow", + texture = texture, + texture_rect = rect, + layer = 2, + color = Color.white, + rotation = 180 + }) + scroll_down_indicator_arrow:set_bottom( self._info_texts_panel:h() ) + scroll_down_indicator_arrow:set_right( self._info_texts_panel:w() ) + + end + + if not scroll_bar then + + scroll_bar = self._info_texts_panel:panel({ + name = "scroll_bar", + layer = 3, + h = bar_h, + w = self._info_texts_panel:w() + }) + + end + + if not scroll_bar_box_panel then + + scroll_bar_box_panel = scroll_bar:panel({ + name = "scroll_bar_box_panel", + w = 4, + halign = "scale", + valign = "scale", + }) + + end + + if self._scroll_bar_box_class then + self._scroll_bar_box_class:close() + end + + scroll_height = ( scroll_down_indicator_arrow:bottom() - scroll_up_indicator_arrow:top() ) + scroll_height = scroll_height * self._info_texts_panel:h() / max_height + if current_offset ~= 0 then + scroll_position = -current_offset * self._info_texts_panel:h() / max_height + else + scroll_position = 0 + end + + scroll_bar_box_panel:set_center_x(scroll_bar:w() / 2) + self._scroll_bar_box_class = BoxGuiObject:new(scroll_bar_box_panel, { + sides = { + 2, + 2, + 0, + 0 + } + }) + self._scroll_bar_box_class:set_aligns("scale", "scale") + scroll_bar_box_panel:set_w( scroll_width ) + scroll_bar_box_panel:set_h( scroll_height ) + scroll_bar:set_top( scroll_up_indicator_arrow:top() + scroll_position ) + scroll_bar:set_center_x( scroll_up_indicator_arrow:center_x() - 2 ) + scroll_bar:set_h( scroll_height ) + + scroll_up_indicator_arrow:set_visible( scroll_bar:top() > scroll_up_indicator_arrow:bottom() ) + scroll_down_indicator_arrow:set_visible( scroll_bar:bottom() < scroll_down_indicator_arrow:top() ) + end Hooks:RegisterHook("BlackMarketGUIOnPopulateWeapons") @@ -1516,6 +1648,8 @@ end function BlackMarketGui._update_info_text(self, slot_data, updated_texts, data, scale_override) + self._info_text_first_set = true + local ignore_lock = false local is_renaming_this = false if data ~= nil then @@ -1630,17 +1764,22 @@ function BlackMarketGui._update_info_text(self, slot_data, updated_texts, data, end function BlackMarketGui:mouse_pressed(button, x, y) + if not self._enabled then return end + if self._renaming_item then self:_stop_rename_item() return end + local holding_shift = false local scroll_button_pressed = button == Idstring("mouse wheel up") or button == Idstring("mouse wheel down") local inside_tab_area = self._tab_area_panel:inside(x, y) + if inside_tab_area then + if button == Idstring("mouse wheel down") then self:next_page(true) return @@ -1648,32 +1787,55 @@ function BlackMarketGui:mouse_pressed(button, x, y) self:previous_page(true) return end + + elseif self._info_texts_panel:inside(x, y) and scroll_button_pressed then + + local scroll_speed = small_font_size + self._info_text_scroll_offset = self._info_text_scroll_offset or 0 + if button == Idstring("mouse wheel down") then + self._info_text_scroll_offset = self._info_text_scroll_offset - scroll_speed + self:_update_borders() + return + elseif button == Idstring("mouse wheel up") then + self._info_text_scroll_offset = self._info_text_scroll_offset + scroll_speed + self:_update_borders() + return + end + elseif self._tabs[self._selected] and scroll_button_pressed and self._tabs[self._selected]:mouse_pressed(button, x, y) then + local x, y = self._tabs[self._selected]:selected_slot_center() self._select_rect:set_world_center(x, y) self._select_rect:stop() self._select_rect_box:set_color(Color.white) self._select_rect:set_visible(y > self._tabs[self._selected]._grid_panel:top() and y < self._tabs[self._selected]._grid_panel:bottom()) return + end + if button ~= Idstring("0") then return end + if self._panel:child("back_button"):inside(x, y) then managers.menu:back(true) return end + if self._tab_scroll_table.left_klick and self._tab_scroll_table.left:inside(x, y) then self:previous_page() return end + if self._tab_scroll_table.right_klick and self._tab_scroll_table.right:inside(x, y) then self:next_page() return end + if self._selected_slot and self._selected_slot._equipped_rect then self._selected_slot._equipped_rect:set_alpha(1) end + if self._tab_scroll_panel:inside(x, y) and self._tabs[self._highlighted] and self._tabs[self._highlighted]:inside(x, y) ~= 1 then if self._selected ~= self._highlighted then self:set_selected_tab(self._highlighted) @@ -1686,6 +1848,7 @@ function BlackMarketGui:mouse_pressed(button, x, y) return end end + if self._rename_info_text then local text_button = self._info_texts and self._info_texts[self._rename_info_text] if self._slot_data and text_button and text_button:inside(x, y) then @@ -1695,6 +1858,7 @@ function BlackMarketGui:mouse_pressed(button, x, y) return end end + if self._btns[self._button_highlighted] and self._btns[self._button_highlighted]:inside(x, y) then local data = self._btns[self._button_highlighted]._data if data.callback and (not self._button_press_delay or self._button_press_delay < TimerManager:main():time()) then @@ -1703,11 +1867,12 @@ function BlackMarketGui:mouse_pressed(button, x, y) self._button_press_delay = TimerManager:main():time() + 0.2 end end + if self._selected_slot and self._selected_slot._equipped_rect then self._selected_slot._equipped_rect:set_alpha(0.6) end -end +end Hooks:RegisterHook("BlackMarketGUIOnPopulateBuyMasks") Hooks:RegisterHook("BlackMarketGUIOnPopulateBuyMasksActionList") From 41b1ff8409b3319946dcbc2a7af71e63de80d149 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Sun, 8 Mar 2015 01:14:35 +1100 Subject: [PATCH 38/92] Update to 1.27.0 Added WeaponTweakData hook --- GoonMod/goonbase.lua | 8 ++------ GoonMod/lua/WeaponTweakData.lua | 8 ++++---- GoonMod/mod.txt | 4 +++- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/GoonMod/goonbase.lua b/GoonMod/goonbase.lua index 9104ea6..c3ffd2d 100644 --- a/GoonMod/goonbase.lua +++ b/GoonMod/goonbase.lua @@ -4,7 +4,7 @@ if not _G.GoonBase then _G.GoonBase = {} GoonBase.Version = 100 - GoonBase.GameVersion = "1.26.0" + GoonBase.GameVersion = "1.27.0" GoonBase.SupportedVersion = true GoonBase.Path = "" @@ -71,11 +71,7 @@ GoonBase.HookFiles = { ["lib/managers/menu/menuscenemanager"] = "MenuSceneManager.lua", ["lib/units/enemies/cop/actions/full_body/copactionhurt"] = "CopActionHurt.lua", ["lib/units/equipment/ecm_jammer/ecmjammerbase"] = "ECMJammerBase.lua", - - -- ["lib/units/maskext"] = "MaskExt.lua", - -- ["lib/units/beings/player/playerinventory"] = "PlayerInventory.lua", - -- ["lib/network/networkgame"] = "NetworkGame.lua", - -- ["lib/setups/setup"] = "Setup.lua", + ["lib/tweak_data/weapontweakdata"] = "WeaponTweakData.lua", } diff --git a/GoonMod/lua/WeaponTweakData.lua b/GoonMod/lua/WeaponTweakData.lua index cd0d723..d04fbe2 100644 --- a/GoonMod/lua/WeaponTweakData.lua +++ b/GoonMod/lua/WeaponTweakData.lua @@ -1,8 +1,8 @@ CloneClass( WeaponTweakData ) -Hooks:RegisterHook("WeaponTweakDataInitNewWeapons") -function WeaponTweakData._init_new_weapons(self, ...) - self.orig._init_new_weapons(self, arg) - Hooks:Call("WeaponTweakDataInitNewWeapons") +Hooks:RegisterHook("WeaponTweakDataPostInitPlayerWeaponData") +function WeaponTweakData._init_data_player_weapons(self, tweak_data) + self.orig._init_data_player_weapons(self, tweak_data) + Hooks:Call("WeaponTweakDataPostInitPlayerWeaponData", self, tweak_data) end diff --git a/GoonMod/mod.txt b/GoonMod/mod.txt index eaca2ef..6a28e3a 100644 --- a/GoonMod/mod.txt +++ b/GoonMod/mod.txt @@ -9,6 +9,7 @@ }, { "revision" : 0, + "revision_file" : "revision.txt", "identifier" : "goonmodwepcust", "install_dir" : "assets/mod_overrides/", "install_folder" : "GoonModWeaponCustomizer", @@ -61,6 +62,7 @@ { "hook_id" : "lib/network/matchmaking/networkmatchmakingsteam", "script_path" : "goonbase.lua" }, { "hook_id" : "lib/managers/menu/menuscenemanager", "script_path" : "goonbase.lua" }, { "hook_id" : "lib/units/enemies/cop/actions/full_body/copactionhurt", "script_path" : "goonbase.lua" }, - { "hook_id" : "lib/units/equipment/ecm_jammer/ecmjammerbase", "script_path" : "goonbase.lua" } + { "hook_id" : "lib/units/equipment/ecm_jammer/ecmjammerbase", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/tweak_data/weapontweakdata", "script_path" : "goonbase.lua" } ] } From 102f84f503dfde376cab7df184c25fb614cf4551 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Sun, 8 Mar 2015 01:16:04 +1100 Subject: [PATCH 39/92] Added PlayerStandard update hook --- GoonMod/lua/PlayerStandard.lua | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/GoonMod/lua/PlayerStandard.lua b/GoonMod/lua/PlayerStandard.lua index aa9c27c..81d8f08 100644 --- a/GoonMod/lua/PlayerStandard.lua +++ b/GoonMod/lua/PlayerStandard.lua @@ -36,3 +36,9 @@ function PlayerStandard._check_action_throw_grenade(self, t, input) end return self.orig._check_action_throw_grenade(self, t, input) end + +Hooks:RegisterHook("PlayerStandardOnUpdate") +function PlayerStandard.update(self, t, dt) + self.orig.update(self, t, dt) + Hooks:Call("PlayerStandardOnUpdate", self, t, dt) +end From 4e1e76fb0e871303aa848d846f7cd9a7ace90ffb Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Sun, 8 Mar 2015 01:19:25 +1100 Subject: [PATCH 40/92] Added generic part name selector for weapon customizer Weapons added in updates and parts without names will attempt to build themselves a generic name based off of known naming conventions Added minigun and RPG part names --- .gitignore | 1 + .../mods/weapon_customization_part_data.lua | 44 ++++++++++++++++++- 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..01a0730 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +GoonMod/mods/akimbo_autoaim.lua diff --git a/GoonMod/mods/weapon_customization_part_data.lua b/GoonMod/mods/weapon_customization_part_data.lua index 82e7387..47e82e8 100644 --- a/GoonMod/mods/weapon_customization_part_data.lua +++ b/GoonMod/mods/weapon_customization_part_data.lua @@ -18,7 +18,8 @@ function WeaponCustomization:GetLocalizedPartName( part_name, part ) if WeaponCustomization.MissingPartLocalizations[ part.name_id ] then return WeaponCustomization.MissingPartLocalizations[ part.name_id ] end - return (part.name_id and managers.localization:text( part.name_id ) or part_name) + local generic_name = part.name_id and managers.localization:text( part.name_id ) or part_name + return WeaponCustomization:GetGenericPartName( generic_name ) end local standard_barrel = "Standard Barrel" @@ -38,6 +39,37 @@ local gadget_adapter = "Gadget Adapter" local optic_adapter = "Optic Adapter" local stock_adapter = "Stock Adapter" +WeaponCustomization.GenericLookups = { + { lookup = "_b_", generic = standard_barrel }, + { lookup = "_m_", generic = standard_magazine }, + { lookup = "_g_", generic = standard_grip }, + { lookup = "_fg_", generic = standard_foregrip }, + { lookup = "_body", generic = standard_body }, + { lookup = "_stock", generic = standard_stock }, + { lookup = "_s_", generic = standard_stock }, + { lookup = "_sl", generic = standard_slide }, + { lookup = "_rail", generic = standard_rail }, + { lookup = "_o_", generic = standard_sights }, + { lookup = "_ns_", generic = standard_compensator }, + { lookup = "_upper", generic = upper_receiver }, + { lookup = "_lower", generic = lower_receiver }, + { lookup = "_fl_adapter", generic = gadget_adapter }, + { lookup = "_o_adapter", generic = optic_adapter }, + { lookup = "_s_adapter", generic = stock_adapter }, + { lookup = "_b", generic = standard_barrel }, + { lookup = "_m", generic = standard_magazine }, + { lookup = "_g", generic = standard_grip }, +} + +function WeaponCustomization:GetGenericPartName( part_name ) + for k, v in ipairs( WeaponCustomization.GenericLookups ) do + if string.find(part_name, v.lookup) then + return v.generic + end + end + return part_name +end + WeaponCustomization.MissingPartLocalizations = { -- Secondaries @@ -370,6 +402,16 @@ WeaponCustomization.MissingPartLocalizations = { ["wpn_fps_shot_shorty_m_extended_short"] = "Extended Magazine", + ["bm_wp_m134_body"] = standard_body, + ["bm_wp_m134_body_upper"] = "Upper Body", + ["bm_wp_m134_m_standard"] = "Magazine Belt", + ["bm_wp_m134_barrel"] = "Barrels", + + ["bm_wp_rpg7_m_rocket"] = "Rocket", + ["bm_wp_rpg7_body"] = standard_body, + ["bm_wp_rpg7_barrel"] = standard_barrel, + ["bm_wp_rpg7_sight"] = standard_sights, + ["bm_wp_upg_vg_ass_smg_afg"] = "AFG", ["bm_wp_upg_vg_ass_smg_stubby"] = "Stubby", ["wpn_fps_upg_fl_ass_peq15_flashlight"] = "Flashlight", From 7e36c4442722565a977d4d78c014e5e7ad36538b Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Mon, 9 Mar 2015 01:51:55 +1100 Subject: [PATCH 41/92] Removed old broken riot shield code --- GoonMod/goonbase.lua | 3 +++ GoonMod/lua/PlayerDamage.lua | 9 +++++++++ GoonMod/lua/PlayerInventory.lua | 33 +++++---------------------------- 3 files changed, 17 insertions(+), 28 deletions(-) diff --git a/GoonMod/goonbase.lua b/GoonMod/goonbase.lua index c3ffd2d..b264c15 100644 --- a/GoonMod/goonbase.lua +++ b/GoonMod/goonbase.lua @@ -72,6 +72,9 @@ GoonBase.HookFiles = { ["lib/units/enemies/cop/actions/full_body/copactionhurt"] = "CopActionHurt.lua", ["lib/units/equipment/ecm_jammer/ecmjammerbase"] = "ECMJammerBase.lua", ["lib/tweak_data/weapontweakdata"] = "WeaponTweakData.lua", + ["lib/units/beings/player/playerinventory"] = "PlayerInventory.lua", + ["lib/tweak_data/skilltreetweakdata"] = "SkillTreeTweakData.lua", + -- ["lib/managers/gameplaycentralmanager"] = "GameplayCentralManager.lua", } diff --git a/GoonMod/lua/PlayerDamage.lua b/GoonMod/lua/PlayerDamage.lua index 5f16f62..12f89e7 100644 --- a/GoonMod/lua/PlayerDamage.lua +++ b/GoonMod/lua/PlayerDamage.lua @@ -18,3 +18,12 @@ function PlayerDamage.on_downed(self) self.orig.on_downed(self) Hooks:Call("PlayerDamageOnDowned", self) end + +Hooks:RegisterHook( "PlayerDamagePreDamageBullet" ) +function PlayerDamage.damage_bullet(self, attack_data) + local r = Hooks:ReturnCall("PlayerDamagePreDamageBullet", self, attack_data) + if r then + return + end + self.orig.damage_bullet(self, attack_data) +end diff --git a/GoonMod/lua/PlayerInventory.lua b/GoonMod/lua/PlayerInventory.lua index 8c0efd7..5cd414a 100644 --- a/GoonMod/lua/PlayerInventory.lua +++ b/GoonMod/lua/PlayerInventory.lua @@ -5,36 +5,13 @@ function PlayerInventory.add_unit_by_factory_name(self, factory_name, equip, ins self.orig.add_unit_by_factory_name(self, factory_name, equip, instant, blueprint, texture_switches) end +Hooks:RegisterHook("PlayerInventoryOnPlaceSelection") function PlayerInventory._place_selection(self, selection_index, is_equip) self.orig._place_selection(self, selection_index, is_equip) - self:create_riot_shield(self._unit) + Hooks:Call("PlayerInventoryOnPlaceSelection", self, selection_index, is_equip) end -function PlayerInventory.create_riot_shield(self, unit) - - local psuccess, perror = pcall(function() - - -- local parent_unit = self._unit:camera()._camera_unit - -- local align_name = Idstring("a_weapon_right") - -- local align_obj = parent_unit:get_object( Idstring("a_weapon_right") ) - - -- self._shield_unit = World:spawn_unit(Idstring("units/payday2/characters/ene_acc_shield_lights/ene_acc_shield_lights"), align_obj:position(), align_obj:rotation()) - -- self._shield_unit:set_enabled(true) - -- self._shield_unit:damage():run_sequence_simple("held_body") - -- parent_unit:link(align_name, self._shield_unit, self._shield_unit:orientation_object():name()) - - -- local body = self._unit:body("mover_blocker") - -- if body then - -- body:set_enabled(false) - -- else - -- Print("could not find body") - -- end - - end) - if not psuccess then - Print("[Error] " .. perror) - end - +Hooks:RegisterHook("PlayerInventoryOnUpdate") +function PlayerInventory.update(self, unit, t, dt) + Hooks:Call("PlayerInventoryOnUpdate", self, unit, t, dt) end - - From f9d1787988b3df2b412a534135d0066751c1734e Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Mon, 9 Mar 2015 01:52:47 +1100 Subject: [PATCH 42/92] Started added melee weapon customization --- GoonMod/lua/MenuSceneManager.lua | 9 +- GoonMod/mod.txt | 4 +- GoonMod/mods/weapon_customization.lua | 134 +++++++++++++----- GoonMod/mods/weapon_customization_menus.lua | 143 +++++++++++--------- 4 files changed, 193 insertions(+), 97 deletions(-) diff --git a/GoonMod/lua/MenuSceneManager.lua b/GoonMod/lua/MenuSceneManager.lua index 9c0f114..9d7adf1 100644 --- a/GoonMod/lua/MenuSceneManager.lua +++ b/GoonMod/lua/MenuSceneManager.lua @@ -4,7 +4,14 @@ CloneClass( MenuSceneManager ) Hooks:RegisterHook("MenuSceneManagerSpawnedItemWeapon") function MenuSceneManager.spawn_item_weapon(self, factory_id, blueprint, texture_switches) local unit = self.orig.spawn_item_weapon(self, factory_id, blueprint, texture_switches) - Hooks:Call("MenuSceneManagerSpawnedItemWeapon", factory_id, blueprint, texture_switches, unit) + Hooks:Call("MenuSceneManagerSpawnedItemWeapon", self, factory_id, blueprint, texture_switches, unit) + return unit +end + +Hooks:RegisterHook("MenuSceneManagerSpawnedMeleeWeapon") +function MenuSceneManager.spawn_melee_weapon_clbk(self, melee_weapon_id) + local unit = self.orig.spawn_melee_weapon_clbk(self, melee_weapon_id) + Hooks:Call("MenuSceneManagerSpawnedMeleeWeapon", self, melee_weapon_id, unit) return unit end diff --git a/GoonMod/mod.txt b/GoonMod/mod.txt index 6a28e3a..294ce61 100644 --- a/GoonMod/mod.txt +++ b/GoonMod/mod.txt @@ -63,6 +63,8 @@ { "hook_id" : "lib/managers/menu/menuscenemanager", "script_path" : "goonbase.lua" }, { "hook_id" : "lib/units/enemies/cop/actions/full_body/copactionhurt", "script_path" : "goonbase.lua" }, { "hook_id" : "lib/units/equipment/ecm_jammer/ecmjammerbase", "script_path" : "goonbase.lua" }, - { "hook_id" : "lib/tweak_data/weapontweakdata", "script_path" : "goonbase.lua" } + { "hook_id" : "lib/tweak_data/weapontweakdata", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/units/beings/player/playerinventory", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/tweak_data/skilltreetweakdata", "script_path" : "goonbase.lua" } ] } diff --git a/GoonMod/mods/weapon_customization.lua b/GoonMod/mods/weapon_customization.lua index 464985a..05b5ccb 100644 --- a/GoonMod/mods/weapon_customization.lua +++ b/GoonMod/mods/weapon_customization.lua @@ -121,18 +121,22 @@ Hooks:Add("BlackMarketGUIOnPreviewWeapon", "BlackMarketGUIOnPreviewWeapon_Weapon end end) -Hooks:Add("MenuSceneManagerSpawnedItemWeapon", "MenuSceneManagerSpawnedItemWeapon_" .. Mod:ID(), function(factory_id, blueprint, texture_switches, spawned_unit) +Hooks:Add("MenuSceneManagerSpawnedItemWeapon", "MenuSceneManagerSpawnedItemWeapon_" .. Mod:ID(), function(menu, factory_id, blueprint, texture_switches, spawned_unit) WeaponCustomization._menu_weapon_preview_unit = spawned_unit if WeaponCustomization._is_previewing then local data = WeaponCustomization._is_previewing["data"] - WeaponCustomization:LoadCurrentWeaponCustomization( data.category, data.slot ) + WeaponCustomization:LoadCurrentWeaponCustomization( data ) WeaponCustomization._is_previewing = nil end end) +Hooks:Add("MenuSceneManagerSpawnedMeleeWeapon", "MenuSceneManagerSpawnedMeleeWeapon_" .. Mod:ID(), function(menu, melee_weapon_id, spawned_unit) + WeaponCustomization._menu_weapon_preview_unit = spawned_unit +end) + Hooks:Add("NewRaycastWeaponBasePostAssemblyComplete", "NewRaycastWeaponBasePostAssemblyComplete_WeaponCustomization", function(weapon, clbk, parts, blueprint) WeaponCustomization:LoadEquippedWeaponCustomizations( weapon ) end) @@ -196,10 +200,15 @@ function WeaponCustomization:AddCustomizablePart( part_id ) table.insert( managers.blackmarket._customizing_weapon_parts, tbl ) end -function WeaponCustomization:CreateCustomizablePartsList( weapon ) +function WeaponCustomization:CreateCustomizablePartsList( weapon, is_melee ) -- Clear weapon parts managers.blackmarket._customizing_weapon_parts = {} + + if is_melee then + return + end + local blueprint_parts = {} -- Add blueprint parts @@ -280,7 +289,7 @@ function WeaponCustomization:UpdateWeaponPartsWithMod( category, mod_id, parts_t end -- Get parts to modify - if not parts_table then + if not parts_table and managers.blackmarket._customizing_weapon_parts then parts_table = {} for k, v in ipairs( managers.blackmarket._customizing_weapon_parts ) do if v.modifying then @@ -290,16 +299,33 @@ function WeaponCustomization:UpdateWeaponPartsWithMod( category, mod_id, parts_t end -- Modify parts - for k, v in pairs( parts_table ) do + local weapon_name = managers.blackmarket._customizing_weapon_data.name + local weapon_category = managers.blackmarket._customizing_weapon_data.category + if parts_table and (weapon_category == "primaries" or weapon_category == "secondaries") then + + for k, v in pairs( parts_table ) do + + -- Update category mod + if v[ category ] then + v[ category ] = mod_id + end + + -- Update part visuals + local color_data = tweak_data.blackmarket.colors[ v["colors"] ] + WeaponCustomization:UpdateWeapon( v["materials"], v["textures"], color_data.colors[1], color_data.colors[2], { [v.id] = true } ) - -- Update category mod - if v[ category ] then - v[ category ] = mod_id end - -- Update part visuals - local color_data = tweak_data.blackmarket.colors[ v["colors"] ] - WeaponCustomization:UpdateWeapon( v["materials"], v["textures"], color_data.colors[1], color_data.colors[2], { [v.id] = true } ) + else + + local wep = managers.blackmarket._global.melee_weapons[weapon_name] + if wep.visual_blueprint then + + local vis_parts = managers.blackmarket._selected_weapon_parts + local color_data = tweak_data.blackmarket.colors[ vis_parts["colors"] ] + WeaponCustomization:UpdateWeapon( vis_parts["materials"], vis_parts["textures"], color_data.colors[1], color_data.colors[2], { ["melee"] = true } ) + + end end @@ -347,7 +373,11 @@ function WeaponCustomization:UpdateWeapon( material_id, pattern_id, tint_color_a weapon_base = managers.player:local_player():inventory():equipped_unit():base() end if self._menu_weapon_preview_unit and alive( self._menu_weapon_preview_unit ) then - weapon_base = self._menu_weapon_preview_unit:base() + weapon_base = self._menu_weapon_preview_unit.base and self._menu_weapon_preview_unit:base() or self._menu_weapon_preview_unit + end + + if self._menu_weapon_preview_unit then + SaveTable( self._menu_weapon_preview_unit.__index, "_menu_weapon_preview_unit.txt" ) end end @@ -376,17 +406,30 @@ function WeaponCustomization:UpdateWeapon( material_id, pattern_id, tint_color_a self._textures = {} -- Find materials - for k, v in pairs( weapon_base._parts ) do - if v.unit and ( (parts_table and parts_table[k]) or not parts_table ) then - - local materials = v.unit:get_objects_by_type(Idstring("material")) - for _, m in ipairs(materials) do - if m:variable_exists(Idstring("tint_color_a")) then - table.insert(self._materials, m) + if type(weapon_base._parts) == "table" then + + for k, v in pairs( weapon_base._parts ) do + if v.unit and ( (parts_table and parts_table[k]) or not parts_table ) then + + local materials = v.unit:get_objects_by_type(Idstring("material")) + for _, m in ipairs(materials) do + if m:variable_exists(Idstring("tint_color_a")) then + table.insert(self._materials, m) + end end + end + end + + else + local materials = weapon_base:get_objects_by_type(Idstring("material")) + for _, m in ipairs(materials) do + if m:variable_exists(Idstring("tint_color_a")) then + table.insert(self._materials, m) + end end + end -- Material @@ -466,9 +509,10 @@ function WeaponCustomization:SaveCurrentWeaponCustomization() end -- Get weapon - local weapon_category = managers.blackmarket._customizing_weapon_data.category - local weapon_slot = managers.blackmarket._customizing_weapon_data.slot - local weapon = managers.blackmarket._global.crafted_items[weapon_category][weapon_slot] + local data = managers.blackmarket._customizing_weapon_data + local weapon_category = data.category + local weapon_slot = data.slot + local weapon = WeaponCustomization:GetWeaponTableFromInventory( data ) if not weapon then Print("[Error] Could not save weapon customization, no weapon found in category '", weapon_category, "' slot '", weapon_slot, "'") @@ -510,15 +554,19 @@ function WeaponCustomization:SaveCurrentWeaponCustomization() end -function WeaponCustomization:LoadCurrentWeaponCustomization( category, slot ) +function WeaponCustomization:LoadCurrentWeaponCustomization( data ) + + data = data or managers.blackmarket._customizing_weapon_data -- Get weapon - local weapon_category = category or managers.blackmarket._customizing_weapon_data.category - local weapon_slot = slot or managers.blackmarket._customizing_weapon_data.slot + local weapon_name = data.name + local weapon_category = data.category + local weapon_slot = data.slot if not weapon_category or not weapon_slot then Print("[Error] Could not load weapon customization, could not find category or slot") + return end - local weapon = managers.blackmarket._global.crafted_items[weapon_category][weapon_slot] + local weapon = WeaponCustomization:GetWeaponTableFromInventory( data ) if not weapon then Print("[Error] Could not load weapon customization, no weapon found in category '", weapon_category, "' slot '", weapon_slot, "'") @@ -527,11 +575,17 @@ function WeaponCustomization:LoadCurrentWeaponCustomization( category, slot ) -- Create default blueprint if it doesn't exist if not weapon.visual_blueprint then + weapon.visual_blueprint = {} - local weapon = managers.blackmarket._global.crafted_items[category][slot] - for k, v in pairs( weapon.blueprint ) do - weapon.visual_blueprint[v] = clone( WeaponCustomization._default_part_visual_blueprint ) + + if weapon.blueprint then + for k, v in pairs( weapon.blueprint ) do + weapon.visual_blueprint[v] = clone( WeaponCustomization._default_part_visual_blueprint ) + end + else + weapon.visual_blueprint["melee"] = clone( WeaponCustomization._default_part_visual_blueprint ) end + end -- Load and apply blueprint @@ -539,10 +593,21 @@ function WeaponCustomization:LoadCurrentWeaponCustomization( category, slot ) end + +function WeaponCustomization:GetWeaponTableFromInventory( data ) + local category = data.category + if category == "primaries" or category == "secondaries" then + return managers.blackmarket._global.crafted_items[category][data.slot] + end + if category == "melee_weapons" then + return managers.blackmarket._global.melee_weapons[data.name] + end + return nil +end + function WeaponCustomization:LoadWeaponCustomizationFromBlueprint( blueprint, unit_override ) if not blueprint then - blueprint = clone( WeaponCustomization._default_part_visual_blueprint ) Print("[Warning] Could not load weapon customization, no visual blueprint specified") return end @@ -554,7 +619,7 @@ function WeaponCustomization:LoadWeaponCustomizationFromBlueprint( blueprint, un local blackmarket_color = v["colors"] local color = tweak_data.blackmarket.colors[ blackmarket_color ] - self:UpdateWeapon( material, pattern, color.colors[1], color.colors[2], { [k] = true }, unit_override ) + self:UpdateWeapon( material, pattern, color.colors[1], color.colors[2], k and { [k] = true } or nil, unit_override ) end @@ -614,4 +679,11 @@ function WeaponCustomization.ClearDataFromSave() end end + -- Erase melee weapons + for k, v in pairs( managers.blackmarket._global["melee_weapons"] ) do + if v.visual_blueprint then + v.visual_blueprint = nil + end + end + end diff --git a/GoonMod/mods/weapon_customization_menus.lua b/GoonMod/mods/weapon_customization_menus.lua index aaee342..1e61fa8 100644 --- a/GoonMod/mods/weapon_customization_menus.lua +++ b/GoonMod/mods/weapon_customization_menus.lua @@ -181,10 +181,9 @@ function WeaponCustomization.weapon_visual_customization_callback(self, data) managers.blackmarket:view_weapon( data.category, data.slot, callback(self, self, "_open_weapon_customization_preview_node", {new_node_data}) ) end if data.category == "melee_weapons" then - -- Melee weapons don't work properly yet, so don't uncomment this unless you want to fix it yourself - -- managers.menu:open_node(self._preview_node_name, {}) - -- managers.blackmarket:preview_melee_weapon(data.name) - -- self:_open_weapon_customization_preview_node( {new_node_data} ) + managers.menu:open_node(self._preview_node_name, {}) + managers.blackmarket:preview_melee_weapon(data.name) + self:_open_weapon_customization_preview_node( {new_node_data} ) end end @@ -196,9 +195,9 @@ function WeaponCustomization._open_weapon_customization_preview_node(self, data) local category = data[1].weapon_slot_data.category local slot = data[1].weapon_slot_data.slot - local weapon = managers.blackmarket._global.crafted_items[category][slot] - - WeaponCustomization:CreateCustomizablePartsList( weapon ) + local weapon = WeaponCustomization:GetWeaponTableFromInventory( data[1].weapon_slot_data ) + + WeaponCustomization:CreateCustomizablePartsList( weapon, category == "melee_weapons" ) managers.blackmarket._selected_weapon_parts = clone( WeaponCustomization._default_part_visual_blueprint ) WeaponCustomization._controller_index = { @@ -207,7 +206,7 @@ function WeaponCustomization._open_weapon_customization_preview_node(self, data) } managers.menu:open_node("blackmarket_mask_node", data) - WeaponCustomization:LoadCurrentWeaponCustomization( category, slot ) + WeaponCustomization:LoadCurrentWeaponCustomization( managers.blackmarket._customizing_weapon_data ) WeaponCustomization:Temp_CheckOverridesInstalled() @@ -253,11 +252,11 @@ Hooks:Add("BlackMarketGUIOnPopulateWeaponActionList", "BlackMarketGUIOnPopulateW end end) --- Hooks:Add("BlackMarketGUIOnPopulateMeleeWeaponActionList", "BlackMarketGUIOnPopulateMeleeWeaponActionList_WeaponCustomization", function(gui, data) --- if data.unlocked then --- table.insert(data, "w_visual_customize") --- end --- end) +Hooks:Add("BlackMarketGUIOnPopulateMeleeWeaponActionList", "BlackMarketGUIOnPopulateMeleeWeaponActionList_WeaponCustomization", function(gui, data) + if data.unlocked then + table.insert(data, "w_visual_customize") + end +end) Hooks:Add("BlackMarketGUIOnPopulateWeapons", "BlackMarketGUIOnPopulateWeapons_WeaponCustomization", function(gui, category, data) if managers.blackmarket._customizing_weapon then @@ -330,72 +329,76 @@ Hooks:Add("BlackMarketGUIUpdateInfoText", "BlackMarketGUIUpdateInfoText_WeaponCu local weapon_data = managers.blackmarket._customizing_weapon_data local category = weapon_data.category local slot = weapon_data.slot - local weapon = managers.blackmarket._global.crafted_items[category][slot] + local weapon = WeaponCustomization:GetWeaponTableFromInventory( weapon_data ) - updated_texts[2].text = managers.localization:to_upper_text("wc_modifying_parts") .. "\n" - updated_texts[3].text = managers.localization:to_upper_text("wc_not_modifying_parts") .. "\n" + if not WeaponCustomization:_IsCustomizingMeleeWeapon() then - if WeaponCustomization:IsUsingController() then - updated_texts[2].text = BTN_LT .. " " .. updated_texts[2].text - updated_texts[3].text = BTN_RT .. " " .. updated_texts[3].text - end + updated_texts[2].text = managers.localization:to_upper_text("wc_modifying_parts") .. "\n" + updated_texts[3].text = managers.localization:to_upper_text("wc_not_modifying_parts") .. "\n" - local num_modifying = 0 - local num_not_modifying = 0 - for k, v in pairs( blackmarket._customizing_weapon_parts ) do - if v and v.modifying then - num_modifying = num_modifying + 1 - else - num_not_modifying = num_not_modifying + 1 + if WeaponCustomization:IsUsingController() then + updated_texts[2].text = BTN_LT .. " " .. updated_texts[2].text + updated_texts[3].text = BTN_RT .. " " .. updated_texts[3].text end - end - local modifying_lines = 0 - local not_modifying_lines = 0 - for k, v in pairs( blackmarket._customizing_weapon_parts ) do + local num_modifying = 0 + local num_not_modifying = 0 + for k, v in pairs( blackmarket._customizing_weapon_parts ) do + if v and v.modifying then + num_modifying = num_modifying + 1 + else + num_not_modifying = num_not_modifying + 1 + end + end - if v.id then - local part = tweak_data.weapon.factory.parts[v.id] - if part then + local modifying_lines = 0 + local not_modifying_lines = 0 + for k, v in pairs( blackmarket._customizing_weapon_parts ) do - local part_name = WeaponCustomization:_GetLocalizedPartName(v.id, part) - local modifying = (v and v.modifying or false) - local part_index = modifying and 2 or 3 + if v.id then + local part = tweak_data.weapon.factory.parts[v.id] + if part then - if WeaponCustomization:IsUsingController() then + local part_name = WeaponCustomization:_GetLocalizedPartName(v.id, part) + local modifying = (v and v.modifying or false) + local part_index = modifying and 2 or 3 - if v and v.modifying then - modifying_lines = modifying_lines + 1 - else - not_modifying_lines = not_modifying_lines + 1 - end + if WeaponCustomization:IsUsingController() then - -- Clamp values - if WeaponCustomization._controller_index.modifying > num_modifying then - WeaponCustomization._controller_index.modifying = 1 - end - if WeaponCustomization._controller_index.not_modifying > num_not_modifying then - WeaponCustomization._controller_index.not_modifying = 1 - end + if v and v.modifying then + modifying_lines = modifying_lines + 1 + else + not_modifying_lines = not_modifying_lines + 1 + end - -- Place markers on appropriate lines - local ind = WeaponCustomization:_GetIndexFromLine(modifying and modifying_lines or not_modifying_lines, modifying) - if modifying then - if WeaponCustomization._controller_index.modifying == modifying_lines then - part_name = BTN_X .. " " .. part_name + -- Clamp values + if WeaponCustomization._controller_index.modifying > num_modifying then + WeaponCustomization._controller_index.modifying = 1 end - else - if WeaponCustomization._controller_index.not_modifying == not_modifying_lines then - part_name = BTN_Y .. " " .. part_name + if WeaponCustomization._controller_index.not_modifying > num_not_modifying then + WeaponCustomization._controller_index.not_modifying = 1 end - end - end - part_name = " " .. part_name .. "\n" + -- Place markers on appropriate lines + local ind = WeaponCustomization:_GetIndexFromLine(modifying and modifying_lines or not_modifying_lines, modifying) + if modifying then + if WeaponCustomization._controller_index.modifying == modifying_lines then + part_name = BTN_X .. " " .. part_name + end + else + if WeaponCustomization._controller_index.not_modifying == not_modifying_lines then + part_name = BTN_Y .. " " .. part_name + end + end - updated_texts[ part_index ].text = updated_texts[ part_index ].text .. part_name + end + part_name = " " .. part_name .. "\n" + updated_texts[ part_index ].text = updated_texts[ part_index ].text .. part_name + + end end + end end @@ -414,10 +417,10 @@ Hooks:Add("BlackMarketGUIUpdateInfoText", "BlackMarketGUIUpdateInfoText_WeaponCu end -- Selected Mod - updated_texts[4].text = "\n" + updated_texts[4].text = WeaponCustomization:_IsCustomizingMeleeWeapon() and "" or "\n" if slot_data then - updated_texts[4].text = "\n" .. managers.localization:to_upper_text("wc_highlighted_mod") .. "\n" .. slot_data.name_localized + updated_texts[4].text = updated_texts[4].text .. managers.localization:to_upper_text("wc_highlighted_mod") .. "\n" .. slot_data.name_localized if not slot_data.unlocked or (type(slot_data.unlocked) == "number" and slot_data.unlocked <= 0) then self._info_texts_color[5] = tweak_data.screen_colors.important_1 @@ -673,6 +676,18 @@ function WeaponCustomization:_GetIndexFromLine(line, modifying) end +function WeaponCustomization:_IsCustomizingMeleeWeapon() + if managers.blackmarket._customizing_weapon_data then + local weapon_data = managers.blackmarket._customizing_weapon_data + local category = weapon_data.category + if category == "primaries" or category == "secondaries" then + return false + else + return true + end + end +end + -- Clear Weapon Function function WeaponCustomization:AdvancedToggleWeaponSpin() From c525322001952962dba4f627a8ec9eabafd21c65 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Mon, 9 Mar 2015 01:54:02 +1100 Subject: [PATCH 43/92] Mutator naming works again in menus Added flag for ignoring certain mutators in the randomizer Added RandomizerIgnore flag to Instagib --- GoonMod/mutators/base_mutator.lua | 11 ++++------- GoonMod/mutators/mutator_instagib.lua | 3 ++- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/GoonMod/mutators/base_mutator.lua b/GoonMod/mutators/base_mutator.lua index 0bda2f8..ba68458 100644 --- a/GoonMod/mutators/base_mutator.lua +++ b/GoonMod/mutators/base_mutator.lua @@ -10,6 +10,7 @@ BaseMutator.MenuPrefix = "toggle_mutator_" BaseMutator.MenuSuffix = "" BaseMutator.HideInOptionsMenu = false BaseMutator.AllPlayersRequireMod = false +BaseMutator.IsAllowedInRandomizer = true BaseMutator.Incompatibilities = {} BaseMutator._AllPlayersRequirementText = "\nWarning: All players must have GoonMod and Mutators installed for this mutator to work properly!" @@ -39,13 +40,9 @@ function BaseMutator:SetupLocalization() self.OptionsDesc = self.OptionsDesc .. self._AllPlayersRequirementText end - if managers.localization then - managers.localization:add_localized_strings({ - [ self:GetName() ] = self.OptionsName, - [ self:GetDesc() ] = self.OptionsDesc, - [ self:GetOriginalDesc() ] = self.OptionsDescOrig, - }) - end + Mutators:RegisterLocalization( self:GetName(), self.OptionsName ) + Mutators:RegisterLocalization( self:GetDesc(), self.OptionsDesc ) + Mutators:RegisterLocalization( self:GetOriginalDesc(), self.OptionsDescOrig ) end diff --git a/GoonMod/mutators/mutator_instagib.lua b/GoonMod/mutators/mutator_instagib.lua index 4af2b04..4d097ca 100644 --- a/GoonMod/mutators/mutator_instagib.lua +++ b/GoonMod/mutators/mutator_instagib.lua @@ -2,8 +2,9 @@ local Mutator = class(BaseMutator) Mutator.Id = "Instagib" Mutator.OptionsName = "Instagib" -Mutator.OptionsDesc = "All weapon damage is amplified to lethal levels" +Mutator.OptionsDesc = "All weapon damage is amplified to lethal levels." Mutator.AllPlayersRequireMod = true +BaseMutator.IsAllowedInRandomizer = false Mutator._RWChkID = "NewRaycastWeaponBase_" .. Mutator.Id Mutator._RWChkIDPost = "NewRaycastWeaponBase_PostHook_" .. Mutator.Id From 056af65f918835367965b41ae03b740a45c6eebe Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Mon, 9 Mar 2015 01:54:21 +1100 Subject: [PATCH 44/92] Update gitignore for Riot Shield mod --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 01a0730..e4ff325 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ GoonMod/mods/akimbo_autoaim.lua +GoonMod/mods/riot_shields.lua From 2172b0b21922576b8c4c13c2b29acca6db1ffb23 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Mon, 9 Mar 2015 02:00:20 +1100 Subject: [PATCH 45/92] Added post initialize hook for SkillTreeTweakData Added Jacket perk deck to gitignore --- .gitignore | 1 + GoonMod/lua/SkillTreeTweakData.lua | 8 ++++++++ 2 files changed, 9 insertions(+) create mode 100644 GoonMod/lua/SkillTreeTweakData.lua diff --git a/.gitignore b/.gitignore index e4ff325..7699948 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ GoonMod/mods/akimbo_autoaim.lua GoonMod/mods/riot_shields.lua +GoonMod/mods/perkdeck_jacket.lua diff --git a/GoonMod/lua/SkillTreeTweakData.lua b/GoonMod/lua/SkillTreeTweakData.lua new file mode 100644 index 0000000..5905964 --- /dev/null +++ b/GoonMod/lua/SkillTreeTweakData.lua @@ -0,0 +1,8 @@ + +CloneClass( SkillTreeTweakData ) + +Hooks:RegisterHook("SkillTreeTweakDataPostInit") +function SkillTreeTweakData.init( self ) + self.orig.init( self ) + Hooks:Call("SkillTreeTweakDataPostInit", self) +end From d8da78e842e23e3aa36a5e85541d8d488b950c54 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Wed, 11 Mar 2015 22:31:28 +1100 Subject: [PATCH 46/92] Infamy 2.0 items will now show up in the mod shop --- GoonMod/goonbase.lua | 2 +- GoonMod/lua/BlackMarketGUI.lua | 2 +- GoonMod/mods/mod_shop.lua | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/GoonMod/goonbase.lua b/GoonMod/goonbase.lua index b264c15..46a997e 100644 --- a/GoonMod/goonbase.lua +++ b/GoonMod/goonbase.lua @@ -4,7 +4,7 @@ if not _G.GoonBase then _G.GoonBase = {} GoonBase.Version = 100 - GoonBase.GameVersion = "1.27.0" + GoonBase.GameVersion = "1.28.0" GoonBase.SupportedVersion = true GoonBase.Path = "" diff --git a/GoonMod/lua/BlackMarketGUI.lua b/GoonMod/lua/BlackMarketGUI.lua index 797064e..c61d369 100644 --- a/GoonMod/lua/BlackMarketGUI.lua +++ b/GoonMod/lua/BlackMarketGUI.lua @@ -1617,7 +1617,7 @@ function BlackMarketGui._start_page_data(self) name = "bm_menu_characters", category = "characters", on_create_func_name = "populate_characters", - override_slots = {4, 2}, + override_slots = {3, 3}, identifier = self.identifiers.character }) end diff --git a/GoonMod/mods/mod_shop.lua b/GoonMod/mods/mod_shop.lua index 2bb42ab..bc22058 100644 --- a/GoonMod/mods/mod_shop.lua +++ b/GoonMod/mods/mod_shop.lua @@ -183,7 +183,7 @@ function ModShop:IsMaskOrModAllowed(mod, allowance_list) end end - local infamy_lock = mod.infamy_lock + local infamy_lock = tweak_data.blackmarket[mod.category][mod.name].infamy_lock if infamy_lock ~= nil or gv == "infamy" then local is_unlocked = managers.infamy:owned(infamy_lock) or infamy_lock == nil if not is_unlocked then @@ -221,7 +221,7 @@ Hooks:Add("BlackMarketManagerModifyGetInventoryCategory", "BlackMarketManagerMod gv = "infamous" end - if gv == "normal" or gv == "infamous" or ( (gv ~= "normal" and managers.dlc:is_dlc_unlocked(gv)) or ModShop.DLCAlwaysUnlocked[gv] == true ) then + if gv == "normal" or gv == "infamous" or gv == "infamy" or ( (gv ~= "normal" and managers.dlc:is_dlc_unlocked(gv)) or ModShop.DLCAlwaysUnlocked[gv] == true ) then table.insert(data, { id = k, global_value = gv, From 69ea5d1246fbf548049726c23e87f1c51c51c099 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Sat, 14 Mar 2015 19:14:05 +1100 Subject: [PATCH 47/92] Added new OnSpawnMeleeItem hook to FPCameraPlayerBase Renamed StanceEnteredCallback --- GoonMod/lua/FPCameraPlayerBase.lua | 10 ++++++++-- GoonMod/mods/disabled/stat_trak.lua | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/GoonMod/lua/FPCameraPlayerBase.lua b/GoonMod/lua/FPCameraPlayerBase.lua index 91bacc5..63fb633 100644 --- a/GoonMod/lua/FPCameraPlayerBase.lua +++ b/GoonMod/lua/FPCameraPlayerBase.lua @@ -1,8 +1,14 @@ CloneClass( FPCameraPlayerBase ) -Hooks:RegisterHook("FPCameraBaseStanceEnteredCallback") +Hooks:RegisterHook("FPCameraPlayerBaseOnSpawnMeleeItem") +function FPCameraPlayerBase.spawn_melee_item( self ) + self.orig.spawn_melee_item( self ) + Hooks:Call( "FPCameraPlayerBaseOnSpawnMeleeItem", self, self._melee_item_units ) +end + +Hooks:RegisterHook("FPCameraPlayerBaseStanceEnteredCallback") function FPCameraPlayerBase.clbk_stance_entered(self, new_shoulder_stance, new_head_stance, new_vel_overshot, new_fov, new_shakers, stance_mod, duration_multiplier, duration) - Hooks:Call( "FPCameraBaseStanceEnteredCallback", self, new_shoulder_stance, new_head_stance, new_vel_overshot, new_fov, new_shakers, stance_mod, duration_multiplier, duration ) + Hooks:Call( "FPCameraPlayerBaseStanceEnteredCallback", self, new_shoulder_stance, new_head_stance, new_vel_overshot, new_fov, new_shakers, stance_mod, duration_multiplier, duration ) self.orig.clbk_stance_entered(self, new_shoulder_stance, new_head_stance, new_vel_overshot, new_fov, new_shakers, stance_mod, duration_multiplier, duration) end diff --git a/GoonMod/mods/disabled/stat_trak.lua b/GoonMod/mods/disabled/stat_trak.lua index bf171a6..5c89b65 100644 --- a/GoonMod/mods/disabled/stat_trak.lua +++ b/GoonMod/mods/disabled/stat_trak.lua @@ -185,7 +185,7 @@ function StatTrak:CycleMode() end -Hooks:Add("FPCameraBaseStanceEnteredCallback", "FPCameraBaseStanceEnteredCallback_" .. Mod:ID(), function(camera, new_shoulder_stance, new_head_stance, new_vel_overshot, new_fov, new_shakers, stance_mod, duration_multiplier, duration) +Hooks:Add("FPCameraPlayerBaseStanceEnteredCallback", "FPCameraPlayerBaseStanceEnteredCallback_" .. Mod:ID(), function(camera, new_shoulder_stance, new_head_stance, new_vel_overshot, new_fov, new_shakers, stance_mod, duration_multiplier, duration) local psuccess, perror = pcall(function() From a22c2df852c2808c5b48811f7570e912d19e6eae Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Sat, 14 Mar 2015 19:14:34 +1100 Subject: [PATCH 48/92] Update to 1.29.0 --- GoonMod/goonbase.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GoonMod/goonbase.lua b/GoonMod/goonbase.lua index 46a997e..f10727a 100644 --- a/GoonMod/goonbase.lua +++ b/GoonMod/goonbase.lua @@ -4,7 +4,7 @@ if not _G.GoonBase then _G.GoonBase = {} GoonBase.Version = 100 - GoonBase.GameVersion = "1.28.0" + GoonBase.GameVersion = "1.29.0" GoonBase.SupportedVersion = true GoonBase.Path = "" From d7ed6b04f6f754b26e786ad1c2b132c3af76ff10 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Sat, 14 Mar 2015 19:14:53 +1100 Subject: [PATCH 49/92] Added melee weapon visual customization --- GoonMod/mods/weapon_customization.lua | 134 +++++++++++++++++--- GoonMod/mods/weapon_customization_menus.lua | 29 +++-- 2 files changed, 132 insertions(+), 31 deletions(-) diff --git a/GoonMod/mods/weapon_customization.lua b/GoonMod/mods/weapon_customization.lua index 05b5ccb..fa2ca0a 100644 --- a/GoonMod/mods/weapon_customization.lua +++ b/GoonMod/mods/weapon_customization.lua @@ -20,6 +20,7 @@ GoonBase.WeaponCustomization = GoonBase.WeaponCustomization or {} local WeaponCustomization = GoonBase.WeaponCustomization WeaponCustomization.MenuId = "goonbase_weapon_customization_menu" WeaponCustomization._update_queue = {} +WeaponCustomization._melee_save_path = SavePath .. "goonmod_weapon_customization_melee.txt" WeaponCustomization._default_part_visual_blueprint = { ["materials"] = "no_material", @@ -135,6 +136,7 @@ end) Hooks:Add("MenuSceneManagerSpawnedMeleeWeapon", "MenuSceneManagerSpawnedMeleeWeapon_" .. Mod:ID(), function(menu, melee_weapon_id, spawned_unit) WeaponCustomization._menu_weapon_preview_unit = spawned_unit + WeaponCustomization:_open_weapon_customization_preview_node() end) Hooks:Add("NewRaycastWeaponBasePostAssemblyComplete", "NewRaycastWeaponBasePostAssemblyComplete_WeaponCustomization", function(weapon, clbk, parts, blueprint) @@ -153,6 +155,16 @@ Hooks:Add("PlayerStandardStartMaskUp", "PlayerStandardStartMaskUp_WeaponCustomiz end end) +Hooks:Add("FPCameraPlayerBaseOnSpawnMeleeItem", "FPCameraPlayerBaseOnSpawnMeleeItem_WeaponCustomization", function(camera, melee_units) + + if melee_units then + for k, v in pairs( melee_units ) do + WeaponCustomization:LoadEquippedWeaponCustomizations( v, true ) + end + end + +end) + Hooks:Add("BlackMarketGUIOnPopulateMaskMods", "BlackMarketGUIOnPopulateMaskMods_WeaponCustomization", function(gui, data) -- Create "no material" data @@ -366,7 +378,8 @@ function WeaponCustomization:UpdateWeapon( material_id, pattern_id, tint_color_a end -- Find weapon - local weapon_base = unit_override and unit_override:base() or nil + local weapon_base = unit_override or nil + if not weapon_base then if managers.player:local_player() then @@ -376,10 +389,6 @@ function WeaponCustomization:UpdateWeapon( material_id, pattern_id, tint_color_a weapon_base = self._menu_weapon_preview_unit.base and self._menu_weapon_preview_unit:base() or self._menu_weapon_preview_unit end - if self._menu_weapon_preview_unit then - SaveTable( self._menu_weapon_preview_unit.__index, "_menu_weapon_preview_unit.txt" ) - end - end if not weapon_base then @@ -521,10 +530,7 @@ function WeaponCustomization:SaveCurrentWeaponCustomization() -- Setup weapon visual customization save if not weapon.visual_blueprint then - weapon.visual_blueprint = {} - for k, v in ipairs( weapon.blueprint ) do - weapon.visual_blueprint[v] = clone( WeaponCustomization._default_part_visual_blueprint ) - end + WeaponCustomization:BuildDefaultVisualBlueprint( weapon ) end -- Get modified parts @@ -534,6 +540,9 @@ function WeaponCustomization:SaveCurrentWeaponCustomization() parts[v.id] = true end end + if weapon_category == "melee_weapons" then + parts["melee"] = true + end -- Update visual customization for k, v in pairs( parts ) do @@ -552,6 +561,57 @@ function WeaponCustomization:SaveCurrentWeaponCustomization() end + if weapon_category == "primaries" or weapon_category == "secondaries" then + managers.blackmarket._global.crafted_items[weapon_category][weapon_slot] = weapon + end + if weapon_category == "melee_weapons" then + managers.blackmarket._global.melee_weapons[data.name] = weapon + self:SaveMeleeWeaponCustomization() + end + +end + +function WeaponCustomization:LoadMeleeWeaponCustomization() + + local file = io.open( WeaponCustomization._melee_save_path, "r" ) + if file then + + local data = file:read("*all") + file:close() + + data = json.decode( data ) + for k, v in pairs( data ) do + if not managers.blackmarket._global.melee_weapons[k].visual_blueprint then + managers.blackmarket._global.melee_weapons[k].visual_blueprint = v + end + end + + end + +end + +function WeaponCustomization:SaveMeleeWeaponCustomization() + + -- Load all customizations before saving + self:LoadMeleeWeaponCustomization() + + local file = io.open( WeaponCustomization._melee_save_path, "w+" ) + if file then + + local tbl = {} + for k, v in pairs( managers.blackmarket._global.melee_weapons ) do + if v.visual_blueprint then + tbl[k] = v.visual_blueprint + end + end + + file:write( json.encode(tbl) ) + file:close() + + else + Print("[Error] Could not save melee weapon visual customizations...") + end + end function WeaponCustomization:LoadCurrentWeaponCustomization( data ) @@ -573,17 +633,18 @@ function WeaponCustomization:LoadCurrentWeaponCustomization( data ) return end - -- Create default blueprint if it doesn't exist + -- Attempt to load melee weapon blueprints, if it still doesn't exist then build the default one if not weapon.visual_blueprint then - weapon.visual_blueprint = {} + if weapon_category == "melee_weapons" then - if weapon.blueprint then - for k, v in pairs( weapon.blueprint ) do - weapon.visual_blueprint[v] = clone( WeaponCustomization._default_part_visual_blueprint ) + self:LoadMeleeWeaponCustomization() + if not weapon.visual_blueprint then + WeaponCustomization:BuildDefaultVisualBlueprint( weapon ) end + else - weapon.visual_blueprint["melee"] = clone( WeaponCustomization._default_part_visual_blueprint ) + WeaponCustomization:BuildDefaultVisualBlueprint( weapon ) end end @@ -593,6 +654,22 @@ function WeaponCustomization:LoadCurrentWeaponCustomization( data ) end +function WeaponCustomization:BuildDefaultVisualBlueprint( weapon ) + + if not weapon.visual_blueprint then + + weapon.visual_blueprint = {} + if weapon.blueprint then + for k, v in pairs( weapon.blueprint ) do + weapon.visual_blueprint[v] = clone( WeaponCustomization._default_part_visual_blueprint ) + end + else + weapon.visual_blueprint["melee"] = clone( WeaponCustomization._default_part_visual_blueprint ) + end + + end + +end function WeaponCustomization:GetWeaponTableFromInventory( data ) local category = data.category @@ -625,17 +702,34 @@ function WeaponCustomization:LoadWeaponCustomizationFromBlueprint( blueprint, un end -function WeaponCustomization:LoadEquippedWeaponCustomizations( weapon_base ) +function WeaponCustomization:LoadEquippedWeaponCustomizations( weapon_base, is_melee ) local equipped_weapons = { [1] = managers.blackmarket:equipped_primary(), - [2] = managers.blackmarket:equipped_secondary() + [2] = managers.blackmarket:equipped_secondary(), + [3] = managers.blackmarket:equipped_melee_weapon(), } - for k, v in pairs( equipped_weapons ) do - if weapon_base._factory_id == v.factory_id and v.visual_blueprint then - WeaponCustomization:LoadWeaponCustomizationFromBlueprint( v.visual_blueprint, weapon_base._unit ) + -- Load primary and secondary weapons + if weapon_base then + + for k, v in pairs( equipped_weapons ) do + if weapon_base._factory_id == v.factory_id and v.visual_blueprint then + WeaponCustomization:LoadWeaponCustomizationFromBlueprint( v.visual_blueprint, weapon_base._unit:base() ) + end end + + end + + -- Load melee weapon + if is_melee then + + self:LoadMeleeWeaponCustomization() + + local melee_weapon = equipped_weapons[3] + local weapon_blueprint = managers.blackmarket._global.melee_weapons[ melee_weapon ].visual_blueprint + WeaponCustomization:LoadWeaponCustomizationFromBlueprint( weapon_blueprint, weapon_base ) + end end diff --git a/GoonMod/mods/weapon_customization_menus.lua b/GoonMod/mods/weapon_customization_menus.lua index 1e61fa8..c2702a8 100644 --- a/GoonMod/mods/weapon_customization_menus.lua +++ b/GoonMod/mods/weapon_customization_menus.lua @@ -181,9 +181,9 @@ function WeaponCustomization.weapon_visual_customization_callback(self, data) managers.blackmarket:view_weapon( data.category, data.slot, callback(self, self, "_open_weapon_customization_preview_node", {new_node_data}) ) end if data.category == "melee_weapons" then + managers.blackmarket._customizing_weapon_data = new_node_data managers.menu:open_node(self._preview_node_name, {}) managers.blackmarket:preview_melee_weapon(data.name) - self:_open_weapon_customization_preview_node( {new_node_data} ) end end @@ -191,11 +191,17 @@ end function WeaponCustomization._open_weapon_customization_preview_node(self, data) managers.blackmarket._customizing_weapon = true - managers.blackmarket._customizing_weapon_data = data[1].weapon_slot_data + if data then + managers.blackmarket._customizing_weapon_data = data[1].weapon_slot_data + else + data = { managers.blackmarket._customizing_weapon_data } + managers.blackmarket._customizing_weapon_data = managers.blackmarket._customizing_weapon_data.weapon_slot_data + end - local category = data[1].weapon_slot_data.category - local slot = data[1].weapon_slot_data.slot - local weapon = WeaponCustomization:GetWeaponTableFromInventory( data[1].weapon_slot_data ) + local weapon_data = managers.blackmarket._customizing_weapon_data + local category = weapon_data.category + local slot = weapon_data.slot + local weapon = WeaponCustomization:GetWeaponTableFromInventory( weapon_data ) WeaponCustomization:CreateCustomizablePartsList( weapon, category == "melee_weapons" ) managers.blackmarket._selected_weapon_parts = clone( WeaponCustomization._default_part_visual_blueprint ) @@ -206,9 +212,9 @@ function WeaponCustomization._open_weapon_customization_preview_node(self, data) } managers.menu:open_node("blackmarket_mask_node", data) - WeaponCustomization:LoadCurrentWeaponCustomization( managers.blackmarket._customizing_weapon_data ) + WeaponCustomization:LoadCurrentWeaponCustomization( weapon_data ) - WeaponCustomization:Temp_CheckOverridesInstalled() + -- WeaponCustomization:Temp_CheckOverridesInstalled() end @@ -744,17 +750,18 @@ function WeaponCustomization.AdvancedClearWeaponAccept() if managers.blackmarket._customizing_weapon_data then local category = managers.blackmarket._customizing_weapon_data.category - local slot = managers.blackmarket._customizing_weapon_data.slot - local weapon = managers.blackmarket._global.crafted_items[category][slot] + local weapon = WeaponCustomization:GetWeaponTableFromInventory( managers.blackmarket._customizing_weapon_data ) -- Rebuild weapon parts list - WeaponCustomization:CreateCustomizablePartsList( weapon ) + if category ~= "melee_weapons" then + WeaponCustomization:CreateCustomizablePartsList( weapon ) + end -- Clear selected parts managers.blackmarket._selected_weapon_parts = clone( WeaponCustomization._default_part_visual_blueprint ) -- Save - WeaponCustomization:UpdateWeaponPartsWithMod( nil, nil, managers.blackmarket._customizing_weapon_parts, true ) + WeaponCustomization:UpdateWeaponPartsWithMod( nil, nil, managers.blackmarket._customizing_weapon_parts, category ~= "melee_weapons" ) -- Clear data on slot if weapon.visual_blueprint then From b12cfc448244ffa287ac042dd089250e31d82d92 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Mon, 16 Mar 2015 11:58:01 +1100 Subject: [PATCH 50/92] Added code redemption and related dialogs to Extended Inventory mod --- GoonMod/dialogs/RedeemCodeDialog.lua | 161 +++++++++++ GoonMod/dialogs/RedeemCodeItemsDialog.lua | 151 ++++++++++ GoonMod/goonbase.lua | 2 + GoonMod/loc/en.txt | 30 ++ GoonMod/lua/SystemMenuManager.lua | 12 + GoonMod/mod.txt | 4 +- GoonMod/mods/extended_inventory.lua | 327 +++++++++++++++++++++- 7 files changed, 680 insertions(+), 7 deletions(-) create mode 100644 GoonMod/dialogs/RedeemCodeDialog.lua create mode 100644 GoonMod/dialogs/RedeemCodeItemsDialog.lua create mode 100644 GoonMod/lua/SystemMenuManager.lua diff --git a/GoonMod/dialogs/RedeemCodeDialog.lua b/GoonMod/dialogs/RedeemCodeDialog.lua new file mode 100644 index 0000000..350d1b2 --- /dev/null +++ b/GoonMod/dialogs/RedeemCodeDialog.lua @@ -0,0 +1,161 @@ + +core:module("SystemMenuManager") +require("lib/managers/dialogs/GenericDialog") + +RedeemCodeDialog = RedeemCodeDialog or class(GenericDialog) +local tweak_data = _G.tweak_data +local code_color_normal = Color.white +local code_color_no_code = Color.white:with_alpha(0.3) +local max_code_length = 15 + +local function make_fine_text(text) + local x, y, w, h = text:text_rect() + text:set_size(w, h) + text:set_position(math.round(text:x()), math.round(text:y())) + return text:x(), text:y(), w, h +end + +function RedeemCodeDialog:init(manager, data) + + Dialog.init(self, manager, data) + if not self._data.focus_button then + if #self._button_text_list > 0 then + self._data.focus_button = #self._button_text_list + else + self._data.focus_button = 1 + end + end + self._ws = self._data.ws or manager:_get_ws() + -- TODO: Remove this \n spam and find a way to set the height properly + self._panel_script = _G.TextBoxGui:new(self._ws, self._data.title or "", (self._data.text or "") .. "\n\n\n\n ", self._data, { + no_close_legend = true, + use_indicator = data.indicator or data.no_buttons, + is_title_outside = is_title_outside, + }) + self._panel_script:add_background() + self._panel_script:set_layer(tweak_data.gui.DIALOG_LAYER) + self._panel_script:set_centered() + self._panel_script:set_fade(0) + self._controller = self._data.controller or manager:_get_controller() + self._confirm_func = callback(self, self, "button_pressed_callback") + self._cancel_func = callback(self, self, "dialog_cancel_callback") + self._resolution_changed_callback = callback(self, self, "resolution_changed_callback") + managers.viewport:add_resolution_changed_func(self._resolution_changed_callback) + if data.counter then + self._counter = data.counter + self._counter_time = self._counter[1] + end + + self._panel = self._panel_script._panel + + local layer = tweak_data.gui.MOUSE_LAYER - 50 + local w, h = self._panel_script:w() * 0.85, self._panel_script:h() * 0.3 + local x, y = self._panel_script:w() * 0.5, self._panel_script:h() * 0.65 + + self._code_rect = self._panel:rect({ + w = w, + h = h, + }) + self._code_rect:set_color( Color.black:with_alpha(0.5) ) + self._code_rect:set_center( x, y ) + + self._code_text = self._panel:text({ + name = "code_text", + text = managers.localization:text("gm_ex_inv_redeem_code_default"), + font = tweak_data.menu.pd2_large_font, + font_size = tweak_data.menu.pd2_large_font_size, + blend_mode = "add", + layer = layer + }) + self._code_text:set_text( managers.localization:text("gm_ex_inv_redeem_code_default") ) + self._code_text:set_color( code_color_no_code ) + make_fine_text( self._code_text ) + self._code_text:set_center( x, y ) + + if not self._connected_keyboard then + self._ws:connect_keyboard(Input:keyboard()) + self._panel:enter_text(callback(self, self, "enter_text")) + self._panel:key_press(callback(self, self, "key_press")) + self._connected_keyboard = true + end + + self._is_entering_code = true + self._entered_code = "" + +end + +function RedeemCodeDialog:UpdateCodeText() + + if not alive( self._code_text ) then + return + end + + self._code_text:set_text( tostring(self._entered_code):upper() ) + if self._entered_code:is_nil_or_empty() then + self._code_text:set_color( code_color_no_code ) + self._code_text:set_text( managers.localization:text("gm_ex_inv_redeem_code_default") ) + else + self._code_text:set_color( code_color_normal ) + end + + local x, y = self._panel_script:w() * 0.5, self._panel_script:h() * 0.65 + make_fine_text( self._code_text ) + self._code_text:set_center( x, y ) + +end + +function RedeemCodeDialog:enter_text( o, s ) + + if not self._is_entering_code then + return + end + + local n = utf8.len(self._entered_code) + if n < max_code_length then + self._entered_code = self._entered_code .. tostring(s) + self._entered_code = self._entered_code:lower() + end + + self:UpdateCodeText() + +end + +function RedeemCodeDialog:key_press( o, k ) + + if not self._is_entering_code then + return + end + + if k == Idstring("backspace") then + local n = utf8.len(self._entered_code) + self._entered_code = utf8.sub(self._entered_code, 0, math.max(n - 1, 0)) + end + + self:UpdateCodeText() + +end + +function RedeemCodeDialog:close() + + if self._connected_keyboard then + self._ws:disconnect_keyboard() + self._panel:enter_text(nil) + self._panel:key_press(nil) + self._connected_keyboard = false + end + + local code = self._entered_code + RedeemCodeDialog.super.close(self) + _G.GoonBase.ExtendedInventory:EnteredRedeemCode( code ) + +end + +function RedeemCodeDialog:button_pressed_callback() + log("RedeemCodeDialog:button_pressed_callback()") + RedeemCodeDialog.super.button_pressed_callback(self) +end + +function RedeemCodeDialog:dialog_cancel_callback() + log("RedeemCodeDialog:dialog_cancel_callback()") + RedeemCodeDialog.super.dialog_cancel_callback(self) +end diff --git a/GoonMod/dialogs/RedeemCodeItemsDialog.lua b/GoonMod/dialogs/RedeemCodeItemsDialog.lua new file mode 100644 index 0000000..881c25c --- /dev/null +++ b/GoonMod/dialogs/RedeemCodeItemsDialog.lua @@ -0,0 +1,151 @@ + +core:module("SystemMenuManager") +require("lib/managers/dialogs/GenericDialog") + +RedeemCodeItemsDialog = RedeemCodeItemsDialog or class(GenericDialog) +local tweak_data = _G.tweak_data +local redeem_max_items_w = 5 +local item_padding = 8 +local function make_fine_text(text) + local x, y, w, h = text:text_rect() + text:set_size(w, h) + text:set_position(math.round(text:x()), math.round(text:y())) + return text:x(), text:y(), w, h +end + +local function make_fine_text(text) + local x, y, w, h = text:text_rect() + text:set_size(w, h) + text:set_position(math.round(text:x()), math.round(text:y())) + return text:x(), text:y(), w, h +end + +function RedeemCodeItemsDialog:init(manager, data) + + Dialog.init(self, manager, data) + if not self._data.focus_button then + if #self._button_text_list > 0 then + self._data.focus_button = #self._button_text_list + else + self._data.focus_button = 1 + end + end + self._ws = self._data.ws or manager:_get_ws() + -- TODO: Remove this \n spam and find a way to set the height properly + self._panel_script = _G.TextBoxGui:new(self._ws, self._data.title or "", (self._data.text or "") .. "\n\n\n\n\n ", self._data, { + no_close_legend = true, + use_indicator = data.indicator or data.no_buttons, + is_title_outside = is_title_outside, + }) + self._panel_script:add_background() + self._panel_script:set_layer(tweak_data.gui.DIALOG_LAYER) + self._panel_script:set_centered() + self._panel_script:set_fade(0) + self._controller = self._data.controller or manager:_get_controller() + self._confirm_func = callback(self, self, "button_pressed_callback") + self._cancel_func = callback(self, self, "dialog_cancel_callback") + self._resolution_changed_callback = callback(self, self, "resolution_changed_callback") + managers.viewport:add_resolution_changed_func(self._resolution_changed_callback) + if data.counter then + self._counter = data.counter + self._counter_time = self._counter[1] + end + + self._panel = self._panel_script._panel + self._item_panel = self._panel:panel() + + local w, h = self._panel_script:w() * 0.95, self._panel_script:h() * 0.4 + local x, y = self._panel_script:w() * 0.5, self._panel_script:h() * 0.5 + self._item_panel:set_size( w, h ) + self._item_panel:set_center( x, y ) + + self._panel_box = _G.BoxGuiObject:new(self._item_panel, { + sides = { + 1, + 1, + 1, + 1 + } + }) + + if data.items then + + self._code_items = {} + for k, v in pairs( data.items ) do + local item = ExtendedInventoryCodeItem:new( self._item_panel, k, v ) + table.insert( self._code_items, item ) + end + + end + +end + +function RedeemCodeItemsDialog:mouse_moved(o, x, y) + + RedeemCodeItemsDialog.super.mouse_moved(self, o, x, y) + + if self._code_items then + for k, v in pairs( self._code_items ) do + v:check_mouse( x, y ) + end + end + +end + +ExtendedInventoryCodeItem = ExtendedInventoryCodeItem or class() +function ExtendedInventoryCodeItem:init( panel, index, data ) + + local padding = item_padding + local w, h = panel:h() - padding * 2, panel:h() - padding * 2 + local x, y = padding * index + w * (index - 1), padding + + local item_name, item_icon = _G.GoonBase.ExtendedInventory:GetDisplayDataForItem( data ) + if item_name and data.quantity then + item_name = item_name .. ( data.quantity > 1 and " x" .. tostring(data.quantity) or "" ) + end + + self._panel = panel + self._item = panel:bitmap({ + name = "item_" .. index, + texture = item_icon or "guis/textures/pd2/blackmarket/icons/cash", + x = x, + y = y, + w = w, + h = h, + color = Color.white + }) + + self._item_name = panel:text({ + text = item_name or "Item", + font = tweak_data.menu.pd2_small_font, + font_size = tweak_data.menu.pd2_small_font_size, + blend_mode = "add", + color = Color.white, + }) + make_fine_text( self._item_name ) + self._item_name:set_center( x + w * 0.5, y + h ) + self._item_name:set_alpha(0) + + return self + +end + +function ExtendedInventoryCodeItem:destroy() + self._panel:remove( self._item ) + self._panel:remove( self._item_name ) + self._item = nil + self._item_name = nil +end + +function ExtendedInventoryCodeItem:check_mouse(x, y) + + if self._item:inside(x, y) then + if self._item_name:alpha() < 1 then + managers.menu:post_event("highlight") + end + self._item_name:set_alpha( 1 ) + else + self._item_name:set_alpha( 0 ) + end + +end diff --git a/GoonMod/goonbase.lua b/GoonMod/goonbase.lua index f10727a..11c21a9 100644 --- a/GoonMod/goonbase.lua +++ b/GoonMod/goonbase.lua @@ -75,6 +75,8 @@ GoonBase.HookFiles = { ["lib/units/beings/player/playerinventory"] = "PlayerInventory.lua", ["lib/tweak_data/skilltreetweakdata"] = "SkillTreeTweakData.lua", -- ["lib/managers/gameplaycentralmanager"] = "GameplayCentralManager.lua", + ["lib/managers/systemmenumanager"] = "SystemMenuManager.lua", + -- ["lib/managers/challengemanager"] = "ChallengeManager.lua", } diff --git a/GoonMod/loc/en.txt b/GoonMod/loc/en.txt index 692c46c..2e1c0c5 100644 --- a/GoonMod/loc/en.txt +++ b/GoonMod/loc/en.txt @@ -43,6 +43,35 @@ "bm_ex_inv_in_reserve" : "IN RESERVE", "bm_menu_amount_locked" : "NONE IN STOCK", + "gm_ex_inv_redeem_code" : "Redeem Code", + "gm_ex_inv_redeem_code_desc" : "Redeem a unique codes for in-game rewards.", + "gm_ex_inv_redeem_code_default" : "Enter Code", + + "gm_ex_inv_redeem_window_title" : "Redeem Code", + "gm_ex_inv_redeem_window_message" : "Here you can redeem unique codes for in-game rewards. Codes are given out for events, and under special circumstances, keep an eye out for them.", + "gm_ex_inv_redeem_window_accept" : "Redeem", + "gm_ex_inv_redeem_window_cancel" : "Cancel", + + "gm_ex_inv_redeem_contact_title" : "Contacting Server", + "gm_ex_inv_redeem_contact_message" : "Attempting to contact server and redeem your code...", + + "gm_ex_inv_redeem_contact_failed_title" : "Failed to Contact Server", + "gm_ex_inv_redeem_contact_failed_message" : "A connection could not be established to the code redemption server, please try again shortly.", + + "gm_ex_inv_redeem_info_title" : "Redeeming Code", + "gm_ex_inv_redeem_info_message" : "You're about to redeem these items:", + "gm_ex_inv_redeem_info_accept" : "Redeem Now", + "gm_ex_inv_redeem_info_cancel" : "Redeem Later", + + "gm_ex_inv_redeemed_confirm_title" : "Redeemed Code", + "gm_ex_inv_redeemed_confirm_message" : "These have been added to your inventory:", + "gm_ex_inv_redeemed_confirm_accept" : "OK", + + "gm_ex_inv_redeem_invalid_title" : "Invalid Code", + "gm_ex_inv_redeem_invalid_not_found" : "This code does not exist!", + "gm_ex_inv_redeem_invalid_already_used" : "You've already used this code!", + "gm_ex_inv_redeem_invalid_no_uses_left" : "This code has already been used!", + "gm_exinv_gage_coin" : "Gage-Coin", "gm_exinv_gage_coin_desc" : "A single coin of an electronic cryptro-currency designed by Gage himself. When he launched it though, it fell flat and became worthless. However with the rise of Crime.net he's has been selling them to heisters at super inflated prices.\nGage gives one of these to every person who brings him a complete courier assignment.", "gm_exinv_gage_coin_reserve" : " IN WALLET", @@ -244,4 +273,5 @@ "Options_WorldLaserRainbowDesc" : "Enable rainbow instead of the set Hue", "Options_WorldLaserRainbowSpeedTitle" : "Rainbow Speed", "Options_WorldLaserRainbowSpeedDesc" : "Set the speed of the rainbow effect", + } diff --git a/GoonMod/lua/SystemMenuManager.lua b/GoonMod/lua/SystemMenuManager.lua new file mode 100644 index 0000000..68a0dbf --- /dev/null +++ b/GoonMod/lua/SystemMenuManager.lua @@ -0,0 +1,12 @@ + +local Hooks = Hooks +core:module("SystemMenuManager") + +GenericSystemMenuManager.orig = {} +GenericSystemMenuManager.orig.init = GenericSystemMenuManager.init + +Hooks:RegisterHook("GenericSystemMenuManagerPostInit") +function GenericSystemMenuManager.init( self ) + Hooks:Call("GenericSystemMenuManagerPostInit", self) + self.orig.init( self ) +end diff --git a/GoonMod/mod.txt b/GoonMod/mod.txt index 294ce61..b1418cd 100644 --- a/GoonMod/mod.txt +++ b/GoonMod/mod.txt @@ -65,6 +65,8 @@ { "hook_id" : "lib/units/equipment/ecm_jammer/ecmjammerbase", "script_path" : "goonbase.lua" }, { "hook_id" : "lib/tweak_data/weapontweakdata", "script_path" : "goonbase.lua" }, { "hook_id" : "lib/units/beings/player/playerinventory", "script_path" : "goonbase.lua" }, - { "hook_id" : "lib/tweak_data/skilltreetweakdata", "script_path" : "goonbase.lua" } + { "hook_id" : "lib/tweak_data/skilltreetweakdata", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/managers/systemmenumanager", "script_path" : "lua/SystemMenuManager.lua" }, + { "hook_id" : "lib/managers/challengemanager", "script_path" : "lua/ChallengeManager.lua" } ] } diff --git a/GoonMod/mods/extended_inventory.lua b/GoonMod/mods/extended_inventory.lua index c03e541..f4135c6 100644 --- a/GoonMod/mods/extended_inventory.lua +++ b/GoonMod/mods/extended_inventory.lua @@ -20,10 +20,25 @@ _G.GoonBase.ExtendedInventory = _G.GoonBase.ExtendedInventory or {} local ExtendedInv = _G.GoonBase.ExtendedInventory ExtendedInv.InitialLoadComplete = false ExtendedInv.RegisteredItems = {} -ExtendedInv.Items = {} -ExtendedInv.SaveFile = SavePath .. "goonbase_inventory.txt" +ExtendedInv.SaveFile = SavePath .. "goonmod_inventory.txt" ExtendedInv.OldFormatSaveFile = SavePath .. "inventory.ini" +ExtendedInv.Items = {} +ExtendedInv.RedeemedCodes = {} + +ExtendedInv.APIRedeem = "http://api.paydaymods.com/goonmod/redeem/{1}" +ExtendedInv.APIRedeemInfo = "http://api.paydaymods.com/goonmod/redeem_info/{1}" + +-- Menu Layout +local redeem_max_items_w = 5 +local item_padding = 8 +local function make_fine_text(text) + local x, y, w, h = text:text_rect() + text:set_size(w, h) + text:set_position(math.round(text:x()), math.round(text:y())) + return text:x(), text:y(), w, h +end + -- Initialize Hooks:RegisterHook("ExtendedInventoryInitialized") Hooks:Add("GoonBasePostLoadedMods", "GoonBasePostLoadedMods_ExtendedInv", function() @@ -102,6 +117,244 @@ function ExtendedInv:GetReserveText(item) return item.reserve_text or managers.localization:text("bm_ex_inv_in_reserve") end +-- Code Redemption +function ExtendedInv:GetDisplayDataForItem( data ) + + local category = data.category + local name = data.item + + if category == "extended_inv" then + local item_data = self:GetItem( name ) + return managers.localization:text(item_data.name), item_data.texture + end + + local item_data = tweak_data.blackmarket[category][name] + if not item_data then + return nil, nil + end + + if category == "masks" then + local guis_catalog = "guis/" + local bundle_folder = item_data.texture_bundle_folder + if bundle_folder then + guis_catalog = guis_catalog .. "dlcs/" .. tostring(bundle_folder) .. "/" + end + local bitmap_texture = guis_catalog .. "textures/pd2/blackmarket/icons/masks/" .. name + return managers.localization:text(item_data.name_id), bitmap_texture + end + +end + +function ExtendedInv:HasUsedCode( code ) + for k, v in pairs( ExtendedInv.RedeemedCodes ) do + if v == code then + return true + end + end + return false +end + +function ExtendedInv:_ShowCodeRedeemWindow() + + local dialog_data = {} + dialog_data.title = managers.localization:text("gm_ex_inv_redeem_window_title") + dialog_data.text = managers.localization:text("gm_ex_inv_redeem_window_message") + dialog_data.id = "ex_inv_redeem_window" + + local ok_button = {} + ok_button.text = managers.localization:text("gm_ex_inv_redeem_window_accept") + + dialog_data.button_list = {ok_button} + managers.system_menu:show_redeem_code_window( dialog_data ) + +end + +function ExtendedInv:EnteredRedeemCode( code ) + + if code:is_nil_or_empty() then + return + end + + self._code_to_redeem = code + self:ShowContactingServerWindow() + + local api_url = ExtendedInv.APIRedeemInfo:gsub( "{1}", code:lower() ) + dohttpreq( api_url, function(data, id) self:RetrievedServerData( data, id ) end ) + +end + +function ExtendedInv:RetrievedServerData( data, id ) + + managers.system_menu:close("ex_inv_redeem_attempt") + + if data:is_nil_or_empty() then + self:ShowFailedToContactServerWindow() + return + end + + local code_data = json.decode( data ) + if code_data.success then + if not self:HasUsedCode( code_data.code ) then + self:ShowRedeemInfoWindow( code_data.data ) + else + self:ShowCodeRedeemFailureWindow( "already_used" ) + end + else + self:ShowCodeRedeemFailureWindow( code_data.code ) + end + +end + +function ExtendedInv:ShowContactingServerWindow() + local dialog_data = {} + dialog_data.title = managers.localization:text("gm_ex_inv_redeem_contact_title") + dialog_data.text = managers.localization:text("gm_ex_inv_redeem_contact_message") + dialog_data.id = "ex_inv_redeem_attempt" + dialog_data.no_buttons = true + dialog_data.indicator = true + managers.system_menu:show(dialog_data) +end + +function ExtendedInv:ShowFailedToContactServerWindow() + local dialog_data = {} + dialog_data.title = managers.localization:text("gm_ex_inv_redeem_contact_failed_title") + dialog_data.text = managers.localization:text("gm_ex_inv_redeem_contact_failed_message") + dialog_data.id = "ex_inv_redeem_failed" + local ok_button = {} + ok_button.text = managers.localization:text("dialog_ok") + dialog_data.button_list = {ok_button} + managers.system_menu:show(dialog_data) +end + +function ExtendedInv:ShowCodeRedeemFailureWindow( error ) + + local errors = { + ["not_found"] = "gm_ex_inv_redeem_invalid_not_found", + ["no_uses_remaining"] = "gm_ex_inv_redeem_invalid_no_uses_left", + ["already_used"] = "gm_ex_inv_redeem_invalid_already_used" + } + + local dialog_data = {} + dialog_data.title = managers.localization:text("gm_ex_inv_redeem_invalid_title") + dialog_data.text = managers.localization:text( errors[error] ) + dialog_data.id = "ex_inv_redeem_failed" + local ok_button = {} + ok_button.text = managers.localization:text("dialog_ok") + dialog_data.button_list = {ok_button} + managers.system_menu:show(dialog_data) + +end + +function ExtendedInv:ShowRedeemInfoWindow( data ) + + data = json.decode( data ) + + local dialog_data = {} + dialog_data.title = managers.localization:text("gm_ex_inv_redeem_info_title") + dialog_data.text = managers.localization:text("gm_ex_inv_redeem_info_message") + dialog_data.id = "ex_inv_redeem_attempt" + + local ok_button = {} + ok_button.text = managers.localization:text("gm_ex_inv_redeem_info_accept") + ok_button.callback_func = callback(self, self, "RedeemCode") + + local cancel_button = {} + cancel_button.text = managers.localization:text("gm_ex_inv_redeem_info_cancel") + cancel_button.cancel_button = true + + dialog_data.button_list = {ok_button, cancel_button} + dialog_data.items = data + managers.system_menu:show_redeem_code_items_window( dialog_data ) + +end + +function ExtendedInv:RedeemCode() + + if not self._code_to_redeem then + return + end + + local code = self._code_to_redeem + self._code_to_redeem = nil + + self:ShowContactingServerWindow() + + local api_url = ExtendedInv.APIRedeem:gsub( "{1}", code:lower() ) + dohttpreq( api_url, function(data, id) self:RedeemedCode( data, id ) end ) + +end + +function ExtendedInv:RedeemedCode( data, id ) + + managers.system_menu:close("ex_inv_redeem_attempt") + + if data:is_nil_or_empty() then + self:ShowFailedToContactServerWindow() + return + end + + local code_data = json.decode( data ) + code_data.data = json.decode( code_data.data ) + + if code_data.success then + + table.insert( ExtendedInv.RedeemedCodes, code_data.code ) + self:Save() + + self:AddRedeemedItemsToInventory( code_data.data ) + self:ShowRedeemedCodeWindow( code_data.data ) + + else + self:ShowCodeRedeemFailureWindow( code_data.code ) + end + +end + +function ExtendedInv:ShowRedeemedCodeWindow( data ) + + local dialog_data = {} + dialog_data.title = managers.localization:text("gm_ex_inv_redeemed_confirm_title") + dialog_data.text = managers.localization:text("gm_ex_inv_redeemed_confirm_message") + dialog_data.id = "ex_inv_redeemed_items" + + local ok_button = {} + ok_button.text = managers.localization:text("gm_ex_inv_redeemed_confirm_accept") + ok_button.cancel_button = true + + dialog_data.button_list = {ok_button} + dialog_data.items = data + managers.system_menu:show_redeem_code_items_window( dialog_data ) + +end + +function ExtendedInv:AddRedeemedItemsToInventory( data ) + + PrintTable( data ) + + for k, v in pairs( data ) do + + local name = v.item + local category = v.category + local quantity = v.quantity + + if category == "extended_inv" then + self:AddItem(name, quantity) + else + + local entry = tweak_data:get_raw_value("blackmarket", category, name) + if entry then + for i = 1, quantity or 1 do + local global_value = entry.infamous and "infamous" or entry.global_value or entry.dlc or entry.dlcs and entry.dlcs[math.random(#entry.dlcs)] or "normal" + managers.blackmarket:add_to_inventory(global_value, category, name) + end + end + + end + + end + +end + -- Hooks Hooks:Add("BlackMarketGUIPostSetup", "BlackMarketGUIPostSetup_ExtendedInventory", function(gui, is_start_page, component_data) gui.identifiers.extended_inv = Idstring("extended_inv") @@ -214,6 +467,55 @@ Hooks:Add("BlackMarketGUIUpdateInfoText", "BlackMarketGUIUpdateInfoText_Extended end) +Hooks:Add("MenuManagerSetupGoonBaseMenu", "MenuManagerSetupGoonBaseMenu_" .. Mod:ID(), function(menu_manager, menu_nodes) + + -- Menu + MenuCallbackHandler.extended_inv_open_redeem_code = function(this, item) + ExtendedInv:_ShowCodeRedeemWindow() + end + + MenuHelper:AddDivider({ + id = "gm_ex_inv_divider", + menu_id = "goonbase_options_menu", + size = 16, + priority = -99, + }) + + MenuHelper:AddButton({ + id = "gm_ex_inv_redeem_button", + title = "gm_ex_inv_redeem_code", + desc = "gm_ex_inv_redeem_code_desc", + callback = "extended_inv_open_redeem_code", + menu_id = "goonbase_options_menu", + priority = -100, + }) + +end) + +Hooks:Add("GenericSystemMenuManagerPostInit", "GenericSystemMenuManagerPostInit_" .. Mod:ID(), function( menu_manager ) + + local dialog_path = GoonBase.Path .. "dialogs/" + dofile( dialog_path .. "RedeemCodeDialog.lua" ) + dofile( dialog_path .. "RedeemCodeItemsDialog.lua" ) + + SystemMenuManager.GenericSystemMenuManager.GENERIC_REDEEM_CODE_DIALOG = SystemMenuManager.RedeemCodeDialog + SystemMenuManager.GenericSystemMenuManager.REDEEM_CODE_CLASS = SystemMenuManager.RedeemCodeDialog + + SystemMenuManager.GenericSystemMenuManager.GENERIC_REDEEM_CODE_ITEMS_DIALOG = SystemMenuManager.RedeemCodeItemsDialog + SystemMenuManager.GenericSystemMenuManager.REDEEM_CODE_ITEMS_CLASS = SystemMenuManager.RedeemCodeItemsDialog + + SystemMenuManager.GenericSystemMenuManager.show_redeem_code_window = function( self, data ) + local success = self:_show_class(data, self.GENERIC_REDEEM_CODE_DIALOG, self.REDEEM_CODE_CLASS, data.force) + self:_show_result(success, data) + end + + SystemMenuManager.GenericSystemMenuManager.show_redeem_code_items_window = function( self, data ) + local success = self:_show_class(data, self.GENERIC_REDEEM_CODE_ITEMS_DIALOG, self.REDEEM_CODE_ITEMS_CLASS, data.force) + self:_show_result(success, data) + end + +end) + -- Saving and Loading function ExtendedInv:Save( file_name ) @@ -221,8 +523,16 @@ function ExtendedInv:Save( file_name ) file_name = ExtendedInv.SaveFile end + local save_data = { + ["items"] = ExtendedInv.Items, + ["codes"] = ExtendedInv.RedeemedCodes, + } + if #ExtendedInv.RedeemedCodes < 1 then + save_data["codes"] = nil + end + local file = io.open(file_name, "w+") - local data = json.encode( ExtendedInv.Items ) + local data = json.encode( save_data ) data = GoonBase.Utils.Base64:Encode( data ) file:write( data ) file:close() @@ -235,13 +545,17 @@ function ExtendedInv:Load( file_name ) local file = io.open(file_name, "r") if not file then Print( "Could not open GoonMod Extended Inventory save file, attempting to load old format..." ) - ExtendedInv:LoadOldFormat() + if ExtendedInv:LoadOldFormat() then + self:Save() + end return end local file_data = file:read("*all") file_data = GoonBase.Utils.Base64:Decode( file_data ) - ExtendedInv.Items = json.decode( file_data ) + local loaded_data = json.decode( file_data ) + ExtendedInv.Items = loaded_data["items"] or {} + ExtendedInv.RedeemedCodes = loaded_data["codes"] or {} end @@ -253,7 +567,7 @@ function ExtendedInv:LoadOldFormat( file_name ) if not file then Print( "Could not open old format save file (" .. file_name .. ")! Does it exist?" ) - return + return false end local key @@ -294,5 +608,6 @@ function ExtendedInv:LoadOldFormat( file_name ) end file:close() + return true end From f54d92692418725497baedfbf79506b8ee9c3d4e Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Mon, 16 Mar 2015 20:23:04 +1100 Subject: [PATCH 51/92] Added ScalableTextBoxGUI --- GoonMod/dialogs/ScalableTextBoxGUI.lua | 27 ++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 GoonMod/dialogs/ScalableTextBoxGUI.lua diff --git a/GoonMod/dialogs/ScalableTextBoxGUI.lua b/GoonMod/dialogs/ScalableTextBoxGUI.lua new file mode 100644 index 0000000..57ac85b --- /dev/null +++ b/GoonMod/dialogs/ScalableTextBoxGUI.lua @@ -0,0 +1,27 @@ + +ScalableTextBoxGui = ScalableTextBoxGui or class( TextBoxGui ) + +local function make_fine_text(text) + local x, y, w, h = text:text_rect() + text:set_size(w, h) + text:set_position(math.round(text:x()), math.round(text:y())) + return text:x(), text:y(), w, h +end + +function ScalableTextBoxGui:set_size(x, y) + + ScalableTextBoxGui.super.set_size(self, x, y) + + local padding = 10 + local info_area = self._panel:child("info_area") + local buttons_panel = info_area:child("buttons_panel") + local scroll_panel = info_area:child("scroll_panel") + scroll_panel:set_w( info_area:w() - scroll_panel:x() * 2 ) + scroll_panel:set_h( info_area:h() - buttons_panel:h() - padding * 3 ) + + make_fine_text( scroll_panel:child("text") ) + + buttons_panel:set_right( info_area:right() - padding ) + buttons_panel:set_bottom( info_area:bottom() - padding ) + +end From 225bc5fa8b0536ba7b7855dd4423437016e7c17b Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Mon, 16 Mar 2015 20:23:59 +1100 Subject: [PATCH 52/92] Reworked code dialogs to use ScalableTextBoxGUI and removed newline spam hack --- GoonMod/dialogs/RedeemCodeDialog.lua | 20 +++++--------------- GoonMod/dialogs/RedeemCodeItemsDialog.lua | 16 +++++++++------- 2 files changed, 14 insertions(+), 22 deletions(-) diff --git a/GoonMod/dialogs/RedeemCodeDialog.lua b/GoonMod/dialogs/RedeemCodeDialog.lua index 350d1b2..d7f7ba6 100644 --- a/GoonMod/dialogs/RedeemCodeDialog.lua +++ b/GoonMod/dialogs/RedeemCodeDialog.lua @@ -26,16 +26,16 @@ function RedeemCodeDialog:init(manager, data) end end self._ws = self._data.ws or manager:_get_ws() - -- TODO: Remove this \n spam and find a way to set the height properly - self._panel_script = _G.TextBoxGui:new(self._ws, self._data.title or "", (self._data.text or "") .. "\n\n\n\n ", self._data, { + self._panel_script = _G.ScalableTextBoxGui:new(self._ws, self._data.title or "", (self._data.text or ""), self._data, { no_close_legend = true, use_indicator = data.indicator or data.no_buttons, is_title_outside = is_title_outside, }) self._panel_script:add_background() self._panel_script:set_layer(tweak_data.gui.DIALOG_LAYER) - self._panel_script:set_centered() self._panel_script:set_fade(0) + self._panel_script:set_size( 540, 360 ) + self._panel_script:set_centered() self._controller = self._data.controller or manager:_get_controller() self._confirm_func = callback(self, self, "button_pressed_callback") self._cancel_func = callback(self, self, "dialog_cancel_callback") @@ -50,7 +50,7 @@ function RedeemCodeDialog:init(manager, data) local layer = tweak_data.gui.MOUSE_LAYER - 50 local w, h = self._panel_script:w() * 0.85, self._panel_script:h() * 0.3 - local x, y = self._panel_script:w() * 0.5, self._panel_script:h() * 0.65 + local x, y = self._panel_script:w() * 0.5, self._panel_script:h() * 0.525 self._code_rect = self._panel:rect({ w = w, @@ -98,7 +98,7 @@ function RedeemCodeDialog:UpdateCodeText() self._code_text:set_color( code_color_normal ) end - local x, y = self._panel_script:w() * 0.5, self._panel_script:h() * 0.65 + local x, y = self._panel_script:w() * 0.5, self._panel_script:h() * 0.525 make_fine_text( self._code_text ) self._code_text:set_center( x, y ) @@ -149,13 +149,3 @@ function RedeemCodeDialog:close() _G.GoonBase.ExtendedInventory:EnteredRedeemCode( code ) end - -function RedeemCodeDialog:button_pressed_callback() - log("RedeemCodeDialog:button_pressed_callback()") - RedeemCodeDialog.super.button_pressed_callback(self) -end - -function RedeemCodeDialog:dialog_cancel_callback() - log("RedeemCodeDialog:dialog_cancel_callback()") - RedeemCodeDialog.super.dialog_cancel_callback(self) -end diff --git a/GoonMod/dialogs/RedeemCodeItemsDialog.lua b/GoonMod/dialogs/RedeemCodeItemsDialog.lua index 881c25c..95910cd 100644 --- a/GoonMod/dialogs/RedeemCodeItemsDialog.lua +++ b/GoonMod/dialogs/RedeemCodeItemsDialog.lua @@ -4,8 +4,10 @@ require("lib/managers/dialogs/GenericDialog") RedeemCodeItemsDialog = RedeemCodeItemsDialog or class(GenericDialog) local tweak_data = _G.tweak_data +local item_size = 80 local redeem_max_items_w = 5 local item_padding = 8 + local function make_fine_text(text) local x, y, w, h = text:text_rect() text:set_size(w, h) @@ -31,16 +33,16 @@ function RedeemCodeItemsDialog:init(manager, data) end end self._ws = self._data.ws or manager:_get_ws() - -- TODO: Remove this \n spam and find a way to set the height properly - self._panel_script = _G.TextBoxGui:new(self._ws, self._data.title or "", (self._data.text or "") .. "\n\n\n\n\n ", self._data, { + self._panel_script = _G.ScalableTextBoxGui:new(self._ws, self._data.title or "", (self._data.text or ""), self._data, { no_close_legend = true, use_indicator = data.indicator or data.no_buttons, is_title_outside = is_title_outside, }) self._panel_script:add_background() self._panel_script:set_layer(tweak_data.gui.DIALOG_LAYER) - self._panel_script:set_centered() self._panel_script:set_fade(0) + self._panel_script:set_size( 500, 340 ) + self._panel_script:set_centered() self._controller = self._data.controller or manager:_get_controller() self._confirm_func = callback(self, self, "button_pressed_callback") self._cancel_func = callback(self, self, "dialog_cancel_callback") @@ -53,9 +55,9 @@ function RedeemCodeItemsDialog:init(manager, data) self._panel = self._panel_script._panel self._item_panel = self._panel:panel() - - local w, h = self._panel_script:w() * 0.95, self._panel_script:h() * 0.4 - local x, y = self._panel_script:w() * 0.5, self._panel_script:h() * 0.5 + + local w, h = item_size * redeem_max_items_w + item_padding * redeem_max_items_w, item_size + item_padding * 2 + local x, y = self._panel_script:w() * 0.5, self._panel_script:h() * 0.465 self._item_panel:set_size( w, h ) self._item_panel:set_center( x, y ) @@ -96,7 +98,7 @@ ExtendedInventoryCodeItem = ExtendedInventoryCodeItem or class() function ExtendedInventoryCodeItem:init( panel, index, data ) local padding = item_padding - local w, h = panel:h() - padding * 2, panel:h() - padding * 2 + local w, h = item_size, item_size local x, y = padding * index + w * (index - 1), padding local item_name, item_icon = _G.GoonBase.ExtendedInventory:GetDisplayDataForItem( data ) From a6eeb6cef2e249af16f3115b8191098ca1da1b0e Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Mon, 16 Mar 2015 20:24:53 +1100 Subject: [PATCH 53/92] Merge new revision changes for mod overrides --- GoonMod/mod.txt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/GoonMod/mod.txt b/GoonMod/mod.txt index b1418cd..56ccde6 100644 --- a/GoonMod/mod.txt +++ b/GoonMod/mod.txt @@ -8,8 +8,7 @@ "identifier" : "goonmod", }, { - "revision" : 0, - "revision_file" : "revision.txt", + "revision" : "revision.txt", "identifier" : "goonmodwepcust", "install_dir" : "assets/mod_overrides/", "install_folder" : "GoonModWeaponCustomizer", @@ -66,7 +65,7 @@ { "hook_id" : "lib/tweak_data/weapontweakdata", "script_path" : "goonbase.lua" }, { "hook_id" : "lib/units/beings/player/playerinventory", "script_path" : "goonbase.lua" }, { "hook_id" : "lib/tweak_data/skilltreetweakdata", "script_path" : "goonbase.lua" }, - { "hook_id" : "lib/managers/systemmenumanager", "script_path" : "lua/SystemMenuManager.lua" }, - { "hook_id" : "lib/managers/challengemanager", "script_path" : "lua/ChallengeManager.lua" } + { "hook_id" : "lib/managers/systemmenumanager", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/managers/challengemanager", "script_path" : "goonbase.lua" } ] } From 005c433ece09889a9fb5e070e49ca7a99b302c1d Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Mon, 16 Mar 2015 20:26:24 +1100 Subject: [PATCH 54/92] Can no longer attempt to use weapon customization while selecting melee weapons in pre-planning --- GoonMod/mods/extended_inventory.lua | 1 + GoonMod/mods/weapon_customization_menus.lua | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/GoonMod/mods/extended_inventory.lua b/GoonMod/mods/extended_inventory.lua index f4135c6..ec375fd 100644 --- a/GoonMod/mods/extended_inventory.lua +++ b/GoonMod/mods/extended_inventory.lua @@ -495,6 +495,7 @@ end) Hooks:Add("GenericSystemMenuManagerPostInit", "GenericSystemMenuManagerPostInit_" .. Mod:ID(), function( menu_manager ) local dialog_path = GoonBase.Path .. "dialogs/" + dofile( dialog_path .. "ScalableTextBoxGUI.lua" ) dofile( dialog_path .. "RedeemCodeDialog.lua" ) dofile( dialog_path .. "RedeemCodeItemsDialog.lua" ) diff --git a/GoonMod/mods/weapon_customization_menus.lua b/GoonMod/mods/weapon_customization_menus.lua index c2702a8..2eccdfe 100644 --- a/GoonMod/mods/weapon_customization_menus.lua +++ b/GoonMod/mods/weapon_customization_menus.lua @@ -232,6 +232,10 @@ end) Hooks:Add("BlackMarketGUIPostSetup", "BlackMarketGUIPostSetup_WeaponCustomization", function(gui, is_start_page, component_data) + if Utils:IsInGameState() then + return + end + gui.customize_weapon_visuals = function(gui, data) WeaponCustomization.weapon_visual_customization_callback(gui, data) end From 366f1e3d640cb5cfbdc0bf2ca4b84097e934eec6 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Wed, 18 Mar 2015 01:00:25 +1100 Subject: [PATCH 55/92] Added PlayerProfileGUIObject and WalletGUIObject hooks --- GoonMod/goonbase.lua | 2 ++ GoonMod/mod.txt | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/GoonMod/goonbase.lua b/GoonMod/goonbase.lua index 11c21a9..bda3e07 100644 --- a/GoonMod/goonbase.lua +++ b/GoonMod/goonbase.lua @@ -77,6 +77,8 @@ GoonBase.HookFiles = { -- ["lib/managers/gameplaycentralmanager"] = "GameplayCentralManager.lua", ["lib/managers/systemmenumanager"] = "SystemMenuManager.lua", -- ["lib/managers/challengemanager"] = "ChallengeManager.lua", + ["lib/managers/menu/playerprofileguiobject"] = "PlayerProfileGUIObject.lua", + ["lib/managers/menu/walletguiobject"] = "WalletGUIObject.lua", } diff --git a/GoonMod/mod.txt b/GoonMod/mod.txt index 56ccde6..6d4fd7c 100644 --- a/GoonMod/mod.txt +++ b/GoonMod/mod.txt @@ -66,6 +66,8 @@ { "hook_id" : "lib/units/beings/player/playerinventory", "script_path" : "goonbase.lua" }, { "hook_id" : "lib/tweak_data/skilltreetweakdata", "script_path" : "goonbase.lua" }, { "hook_id" : "lib/managers/systemmenumanager", "script_path" : "goonbase.lua" }, - { "hook_id" : "lib/managers/challengemanager", "script_path" : "goonbase.lua" } + { "hook_id" : "lib/managers/challengemanager", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/managers/menu/playerprofileguiobject", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/managers/menu/walletguiobject", "script_path" : "goonbase.lua" } ] } From 3d9bcaff5c6e9200323674426eaff99705c524b9 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Wed, 18 Mar 2015 01:00:54 +1100 Subject: [PATCH 56/92] GageCoins will now be displayed on the main player info panel and in the inventory alongside cash --- GoonMod/loc/en.txt | 11 ++-- GoonMod/mods/gage_coins.lua | 121 ++++++++++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+), 7 deletions(-) diff --git a/GoonMod/loc/en.txt b/GoonMod/loc/en.txt index 2e1c0c5..4be4b80 100644 --- a/GoonMod/loc/en.txt +++ b/GoonMod/loc/en.txt @@ -73,6 +73,7 @@ "gm_ex_inv_redeem_invalid_no_uses_left" : "This code has already been used!", "gm_exinv_gage_coin" : "Gage-Coin", + "gm_exinv_gage_coin_plural" : "Gage-Coins", "gm_exinv_gage_coin_desc" : "A single coin of an electronic cryptro-currency designed by Gage himself. When he launched it though, it fell flat and became worthless. However with the rise of Crime.net he's has been selling them to heisters at super inflated prices.\nGage gives one of these to every person who brings him a complete courier assignment.", "gm_exinv_gage_coin_reserve" : " IN WALLET", @@ -80,15 +81,11 @@ "OptionsMenu_GrenadeMarkerDesc" : "Show a HUD marker when a flashbang is deployed", "gm_gms_purchase" : "Buy with Gage Coins", - "gm_gms_purchase_window_title" : "Purchase", - "gm_gms_purchase_window_message" : "You are about to purchase {1}. This will cost you {2} Gage Coin/s.\n\nPurchasing:\n {1}, {2} GC/s\nBalance before purchase:\n {3} GC\nBalance after purchase:\n {4} GC", - "gm_gms_purchase_window_accept" : "Purchase", - "gm_gms_purchase_window_cancel" : "Cancel", + "gm_gms_purchase_window_title" : "Are you sure?", + "gm_gms_purchase_window_message" : "Do you really want to buy '{1}'?\n\nIt will cost you {2} {3}.", "gm_gms_purchase_failed" : "Cannot Purchase", "gm_gms_free_of_charge_message" : "{1} is free of charge and can be applied to as many weapons as you wish.", - "gm_gms_free_of_charge_accept" : "OK", - "gm_gms_cannot_afford_message" : "You cannot purchase {1}, as you do not have enough Gage Coins to afford it. To purchase {1}, you need {2} GC/s.", - "gm_gms_cannot_afford_accept" : "OK", + "gm_gms_cannot_afford_message" : "You cannot purchase {1}, as you do not have enough {3} to afford it. To purchase {1}, you need {2} {3}", "Trading_OptionsMenuTitle" : "Crime.net Cargo", "Trading_OptionsMenuMessage" : "Modify Crime.net Cargo Settings", diff --git a/GoonMod/mods/gage_coins.lua b/GoonMod/mods/gage_coins.lua index e7f29b2..ad7b454 100644 --- a/GoonMod/mods/gage_coins.lua +++ b/GoonMod/mods/gage_coins.lua @@ -67,3 +67,124 @@ Hooks:Add("GageAssignmentManagerOnMissionCompleted", "GageAssignmentManagerOnMis end end) + +Hooks:Add("PlayerProfileGuiObjectPostInit", "PlayerProfileGuiObjectPostInit_" .. Mod:ID(), function( gui, ws ) + + if not ExtendedInv or not GageCoins or not gui._panel then + return + end + if not ExtendedInv:HasItem( GageCoins.CoinID ) then + return + end + + local offshore_text = nil + for k, v in pairs( gui._panel:children() ) do + if v.text then + if string.match( tostring(v:text()), tostring(managers.experience:cash_string(managers.money:offshore())) ) then + offshore_text = v + end + end + end + + if offshore_text then + + local font = tweak_data.menu.pd2_small_font + local font_size = tweak_data.menu.pd2_small_font_size + local gage_coins_text = gui._panel:text({ + text = gui:get_text("gm_exinv_gage_coin_plural") .. ": " .. ExtendedInv:GetItem( GageCoins.CoinID ).amount, + font_size = font_size, + font = font, + color = tweak_data.screen_colors.text + }) + gui:_make_fine_text(gage_coins_text) + gage_coins_text:set_left( math.round(offshore_text:left()) ) + gage_coins_text:set_top( math.round(offshore_text:bottom()) ) + + local skillpoints = managers.skilltree:points() + local skill_text, skill_icon, skill_glow + + if skillpoints > 0 then + + for k, v in pairs( gui._panel:children() ) do + + if v.text then + local skillpoint_text = gui:get_text("menu_spendable_skill_points", { points = tostring(skillpoints) }) + if string.match( tostring(v:text()), tostring(skillpoint_text) ) then + skill_text = v + end + end + + if v.texture_name then + local tex_name = v:texture_name() + if tex_name == Idstring("guis/textures/pd2/shared_skillpoint_symbol") then + skill_icon = v + end + if tex_name == Idstring("guis/textures/pd2/crimenet_marker_glow") then + skill_glow = v + end + end + + end + + skill_text:set_top(math.round(gage_coins_text:bottom())) + if skill_icon then + skill_icon:set_center_y(skill_text:center_y() + 1) + end + if skill_glow then + skill_glow:set_center_y(skill_icon:center_y()) + end + + end + + end + +end) + +Hooks:Add("WalletGuiObjectOnSetWallet", "WalletGuiObjectOnSetWallet_" .. Mod:ID(), function( panel, layer ) + + if not ExtendedInv or not GageCoins then + return + end + + local gage_coins_icon = Global.wallet_panel:bitmap({ + name = "gage_coins_icon", + texture = "guis/textures/pd2/shared_wallet_symbol" + }) + + local text = tostring( ExtendedInv:GetItem( GageCoins.CoinID ).amount ) + local gage_coins_text = Global.wallet_panel:text({ + name = "gage_coins_text", + text = text, + font_size = tweak_data.menu.pd2_small_font_size, + font = tweak_data.menu.pd2_small_font, + color = tweak_data.screen_colors.text + }) + + local money_icon = Global.wallet_panel:child("wallet_money_icon") + local money_text = Global.wallet_panel:child("wallet_money_text") + gage_coins_icon:set_leftbottom( money_text:right() + 8, Global.wallet_panel:h() - 2 ) + gage_coins_text:set_left(gage_coins_icon:right() + 2) + gage_coins_text:set_center_y(gage_coins_icon:center_y()) + gage_coins_text:set_y( math.round(gage_coins_icon:y() - 2) ) + +end) + +Hooks:Add("WalletGuiObjectOnRefresh", "WalletGuiObjectOnRefresh_" .. Mod:ID(), function() + + if Global.wallet_panel then + + local text = tostring( ExtendedInv:GetItem( GageCoins.CoinID ).amount ) + local gage_coins_icon = Global.wallet_panel:child("gage_coins_icon") + local gage_coins_text = Global.wallet_panel:child("gage_coins_text") + local money_icon = Global.wallet_panel:child("wallet_money_icon") + local money_text = Global.wallet_panel:child("wallet_money_text") + + gage_coins_icon:set_leftbottom( money_text:right() + 8, Global.wallet_panel:h() - 2 ) + gage_coins_text:set_text( text ) + gage_coins_text:set_left(gage_coins_icon:right() + 2) + gage_coins_text:set_center_y(gage_coins_icon:center_y()) + gage_coins_text:set_y( math.round(gage_coins_icon:y() - 2) ) + + end + +end) From 66ff0209e8856378f0bac9d63ae5dd9329f04b5a Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Wed, 18 Mar 2015 01:01:25 +1100 Subject: [PATCH 57/92] Adding in files that missed checkin --- GoonMod/lua/PlayerProfileGUIObject.lua | 8 ++++++++ GoonMod/lua/WalletGUIObject.lua | 20 ++++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 GoonMod/lua/PlayerProfileGUIObject.lua create mode 100644 GoonMod/lua/WalletGUIObject.lua diff --git a/GoonMod/lua/PlayerProfileGUIObject.lua b/GoonMod/lua/PlayerProfileGUIObject.lua new file mode 100644 index 0000000..581bcc2 --- /dev/null +++ b/GoonMod/lua/PlayerProfileGUIObject.lua @@ -0,0 +1,8 @@ + +CloneClass( PlayerProfileGuiObject ) + +Hooks:RegisterHook("PlayerProfileGuiObjectPostInit") +function PlayerProfileGuiObject.init(self, ws) + self.orig.init(self, ws) + Hooks:Call( "PlayerProfileGuiObjectPostInit", self, ws ) +end diff --git a/GoonMod/lua/WalletGUIObject.lua b/GoonMod/lua/WalletGUIObject.lua new file mode 100644 index 0000000..328ce96 --- /dev/null +++ b/GoonMod/lua/WalletGUIObject.lua @@ -0,0 +1,20 @@ + +CloneClass( WalletGuiObject ) + +Hooks:RegisterHook("WalletGuiObjectOnSetWallet") +function WalletGuiObject.set_wallet(panel, layer) + WalletGuiObject.orig.set_wallet(panel, layer) + Hooks:Call( "WalletGuiObjectOnSetWallet", panel, layer ) +end + +Hooks:RegisterHook("WalletGuiObjectOnRefresh") +function WalletGuiObject.refresh() + WalletGuiObject.orig.refresh() + Hooks:Call( "WalletGuiObjectOnRefresh" ) +end + +Hooks:RegisterHook("WalletGuiObjectSetObjectVisible") +function WalletGuiObject.set_object_visible(object, visible) + WalletGuiObject.orig.set_object_visible(object, visible) + Hooks:Call( "WalletGuiObjectSetObjectVisible", object, visible ) +end From 9f33531bfb4f1e7b7c7809314160d65e4d058c1a Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Wed, 18 Mar 2015 15:40:36 +1100 Subject: [PATCH 58/92] Update character slots in inventory --- GoonMod/lua/BlackMarketGUI.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GoonMod/lua/BlackMarketGUI.lua b/GoonMod/lua/BlackMarketGUI.lua index c61d369..b76f64a 100644 --- a/GoonMod/lua/BlackMarketGUI.lua +++ b/GoonMod/lua/BlackMarketGUI.lua @@ -1617,7 +1617,7 @@ function BlackMarketGui._start_page_data(self) name = "bm_menu_characters", category = "characters", on_create_func_name = "populate_characters", - override_slots = {3, 3}, + override_slots = {5, 3}, identifier = self.identifiers.character }) end From 6de00707df90c9ad18df55eb23eb3c538c4a164e Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Wed, 18 Mar 2015 15:53:15 +1100 Subject: [PATCH 59/92] Fist and weapon butt melee weapons can't be customized Customizing melee weapons will no longer open two preview nodes Quitting the weapon customization menu will restore the menu colour grading to normal Fixed an error with customizing melee weapons and then previewing them --- GoonMod/mods/weapon_customization_menus.lua | 35 +++++++++++++++------ 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/GoonMod/mods/weapon_customization_menus.lua b/GoonMod/mods/weapon_customization_menus.lua index 2eccdfe..7be041c 100644 --- a/GoonMod/mods/weapon_customization_menus.lua +++ b/GoonMod/mods/weapon_customization_menus.lua @@ -25,6 +25,11 @@ WeaponCustomization._controller_index = { not_modifying = 1, } +WeaponCustomization._invalid_melee_weapons = { + ["weapon"] = true, + ["fists"] = true, +} + local BTN_X = utf8.char(57346) local BTN_Y = utf8.char(57347) local BTN_LT = utf8.char(57354) @@ -169,11 +174,6 @@ function WeaponCustomization.weapon_visual_customization_callback(self, data) weapon_name = data.name_localized } - local params = {} - params.yes_func = callback(self, self, "_dialog_yes", callback(self, self, "_abort_customized_mask_callback")) - params.no_func = callback(self, self, "_dialog_no") - - new_node_data.back_callback = callback(self, self, "_warn_abort_customized_mask_callback", params) new_node_data.blur_fade = self._data.blur_fade new_node_data.weapon_slot_data = data @@ -182,7 +182,6 @@ function WeaponCustomization.weapon_visual_customization_callback(self, data) end if data.category == "melee_weapons" then managers.blackmarket._customizing_weapon_data = new_node_data - managers.menu:open_node(self._preview_node_name, {}) managers.blackmarket:preview_melee_weapon(data.name) end @@ -190,6 +189,10 @@ end function WeaponCustomization._open_weapon_customization_preview_node(self, data) + if not managers.blackmarket._customizing_weapon_data and not data then + return + end + managers.blackmarket._customizing_weapon = true if data then managers.blackmarket._customizing_weapon_data = data[1].weapon_slot_data @@ -199,6 +202,10 @@ function WeaponCustomization._open_weapon_customization_preview_node(self, data) end local weapon_data = managers.blackmarket._customizing_weapon_data + if not weapon_data then + return + end + local category = weapon_data.category local slot = weapon_data.slot local weapon = WeaponCustomization:GetWeaponTableFromInventory( weapon_data ) @@ -211,11 +218,9 @@ function WeaponCustomization._open_weapon_customization_preview_node(self, data) not_modifying = 1, } - managers.menu:open_node("blackmarket_mask_node", data) + managers.menu:open_node( "blackmarket_mask_node", data ) WeaponCustomization:LoadCurrentWeaponCustomization( weapon_data ) - -- WeaponCustomization:Temp_CheckOverridesInstalled() - end Hooks:Add("MenuSceneManagerOverrideSceneTemplate", "MenuSceneManagerOverrideSceneTemplate_WeaponCustomization", function(scene, template, data, custom_name, skip_transition) @@ -263,7 +268,7 @@ Hooks:Add("BlackMarketGUIOnPopulateWeaponActionList", "BlackMarketGUIOnPopulateW end) Hooks:Add("BlackMarketGUIOnPopulateMeleeWeaponActionList", "BlackMarketGUIOnPopulateMeleeWeaponActionList_WeaponCustomization", function(gui, data) - if data.unlocked then + if data.unlocked and not WeaponCustomization._invalid_melee_weapons[data.name] then table.insert(data, "w_visual_customize") end end) @@ -474,6 +479,10 @@ Hooks:Add("MenuUpdate", "MenuUpdate_WeaponCustomization", function(t, dt) end) +Hooks:Add("BlackMarketGUIOnPopulateWeapons", "BlackMarketGUIOnPopulateWeapons_WeaponCustomization", function(gui, category, data) + WeaponCustomization:RestoreMenuColourGrading() +end) + function WeaponCustomization:_UpdateControllerBindings() if WeaponCustomization:IsUsingController() then @@ -731,6 +740,12 @@ function WeaponCustomization:AddvancedToggleColourGrading() end +function WeaponCustomization:RestoreMenuColourGrading() + managers.environment_controller:set_default_color_grading( "color_matrix" ) + managers.environment_controller:refresh_render_settings() + self._previous_colour_grading = nil +end + function WeaponCustomization:AdvancedClearWeaponCheck() local title = managers.localization:text("wc_clear_weapon_title") From bea975e03c3a1c1b9b92a52b44e95e4b5fc83deb Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Wed, 18 Mar 2015 19:37:53 +1100 Subject: [PATCH 60/92] Recoded Gage Mod Shop to add items properly and use less duplicated code --- GoonMod/mods/mod_shop.lua | 555 ++++++++++++++------------------------ 1 file changed, 202 insertions(+), 353 deletions(-) diff --git a/GoonMod/mods/mod_shop.lua b/GoonMod/mods/mod_shop.lua index bc22058..be2cd5b 100644 --- a/GoonMod/mods/mod_shop.lua +++ b/GoonMod/mods/mod_shop.lua @@ -23,25 +23,6 @@ local ExtendedInv = _G.GoonBase.ExtendedInventory ModShop.PurchaseCurrency = "gage_coin" ModShop.CostRegular = 1 ModShop.CostInfamous = 3 - -ModShop.ExclusionList = { - ["nothing"] = true, - ["no_color_no_material"] = true, - ["no_color_full_material"] = true, - ["plastic"] = true, - ["character_locked"] = true, -} - -ModShop.DLCAlwaysUnlocked = { - ["halloween"] = true, -} - -ModShop.MaskAllowanceList = { - ["normal"] = true, - ["halloween"] = true, - ["infamous"] = true, -} - ModShop.MaskPricing = { ["default"] = 5, ["dlc"] = 5, @@ -49,438 +30,306 @@ ModShop.MaskPricing = { ["pd2_clan"] = 3, ["halloween"] = 8, ["infamous"] = 20, - ["infamy"] = 10, + ["infamy"] = 20, } -ModShop.MaskModAllowanceList = { +ModShop.ExclusionList = { + ["nothing"] = true, + ["no_color_no_material"] = true, + ["no_color_full_material"] = true, + ["plastic"] = true, + ["character_locked"] = true, +} + +ModShop.NonDLCGlobalValues = { ["normal"] = true, + ["pd2_clan"] = true, ["halloween"] = true, ["infamous"] = true, + ["infamy"] = true, } --- Blackmarket Menu -Hooks:Add("BlackMarketGUIPostSetup", "BlackMarketGUIPostSetup_" .. Mod:ID(), function(gui, is_start_page, component_data) +function ModShop:IsItemExluded( item ) + return ModShop.ExclusionList[item] or false +end - Hooks:RegisterHook("ModShopAttemptPurchaseWeaponMod") - gui.modshop_purchase_weaponmod_callback = function(self, data) - Hooks:Call("ModShopAttemptPurchaseWeaponMod", data) +function ModShop:IsGlobalValueDLC( gv ) + if not ModShop.NonDLCGlobalValues[gv] then + return true end + return false +end - Hooks:RegisterHook("ModShopAttemptPurchaseMask") - gui.modshop_purchase_mask_callback = function(self, data) - Hooks:Call("ModShopAttemptPurchaseMask", data) - end +function ModShop:IsInfamyLocked( data ) - Hooks:RegisterHook("ModShopAttemptPurchaseMaskPart") - gui.modshop_purchase_mask_part_callback = function(self, data) - Hooks:Call("ModShopAttemptPurchaseMaskPart", data) + local infamy_lock = data.tweak_data.infamy_lock + if infamy_lock then + local is_unlocked = managers.infamy:owned(infamy_lock) + if not is_unlocked then + return true + end end - local wm_modshop = { - prio = 5, - btn = "BTN_BACK", - pc_btn = Idstring("toggle_chat"), - name = "gm_gms_purchase", - callback = callback(gui, gui, "modshop_purchase_weaponmod_callback") - } - - local bm_modshop = { - prio = 5, - btn = "BTN_BACK", - pc_btn = Idstring("toggle_chat"), - name = "gm_gms_purchase", - callback = callback(gui, gui, "modshop_purchase_mask_callback") - } + return false - local mp_modshop = { - prio = 5, - btn = "BTN_BACK", - pc_btn = Idstring("toggle_chat"), - name = "gm_gms_purchase", - callback = callback(gui, gui, "modshop_purchase_mask_part_callback") - } +end - local btn_x = 10 - gui._btns["wm_modshop"] = BlackMarketGuiButtonItem:new(gui._buttons, wm_modshop, btn_x) - gui._btns["bm_modshop"] = BlackMarketGuiButtonItem:new(gui._buttons, bm_modshop, btn_x) - gui._btns["mp_modshop"] = BlackMarketGuiButtonItem:new(gui._buttons, mp_modshop, btn_x) +function ModShop:GetItemPrice( data ) -end) + if data.category == "masks" then + local gv = self:IsGlobalValueDLC( data.global_value ) and "dlc" or data.global_value + return ModShop.MaskPricing[ gv ] + end -Hooks:Add("BlackMarketGUIOnPopulateModsActionList", "BlackMarketGUIOnPopulateModsActionList_" .. Mod:ID(), function(gui, data) - if ModShop:WeaponModAllowed(data) then - table.insert(data, "wm_modshop") + if data.global_value == "infamy" or data.global_value == "infamous" then + return ModShop.CostInfamous end -end) -function ModShop:WeaponModAllowed(mod) + return ModShop.CostRegular - if mod == nil then - return false - end +end - if mod.free_of_charge then - return false +function ModShop:_ReloadBlackMarket() + local blackmarket_gui = managers.menu_component._blackmarket_gui + if blackmarket_gui then + blackmarket_gui:reload() + blackmarket_gui:on_slot_selected( blackmarket_gui._selected_slot ) end +end - for k, v in pairs( tweak_data.dlc ) do - if v.achievement_id ~= nil and v.content ~= nil and v.content.loot_drops ~= nil then - for i, loot in pairs( v.content.loot_drops ) do - if loot.item_entry ~= nil and loot.item_entry == mod.name then - return managers.achievment.handler:has_achievement(v.achievement_id) - end - end - end - end +function ModShop:AttemptItemPurchase( data, weapon_part ) - local gv = mod.global_value - if gv == nil or gv == "normal" then - return true + if not data then + return end - if not managers.dlc:is_dlc_unlocked(gv) then - return false + local verified, purchase_data = self:VerifyItemPurchase( data, weapon_part ) + if verified then + self:ShowItemPurchaseMenu( purchase_data ) end - return true - end -Hooks:Add("BlackMarketGUIOnPopulateBuyMasksActionList", "BlackMarketGUIOnPopulateBuyMasksActionList_" .. Mod:ID(), function(gui, data) - if ModShop:IsMaskOrModAllowed(data, ModShop.MaskAllowanceList) then - table.insert(data, "bm_modshop") +function ModShop:VerifyItemPurchase( data, weapon_part ) + + local name = data.name + local category = weapon_part and "parts" or data.category + + local entry + if weapon_part then + entry = tweak_data:get_raw_value("weapon", "factory", category, name) + else + entry = tweak_data:get_raw_value("blackmarket", category, name) end -end) -Hooks:Add("BlackMarketGUIOnPopulateMaskModsActionList", "BlackMarketGUIOnPopulateMaskModsActionList_" .. Mod:ID(), function(gui, data) - if ModShop:IsMaskOrModAllowed(data, ModShop.MaskModAllowanceList) then - table.insert(data, "mp_modshop") + if not entry then + local str = "[Error] Could not retrieve tweak_data for {1} item '{2}', weapon_part: {3}" + str = str:gsub("{1}", tostring(category)) + str = str:gsub("{2}", tostring(name)) + str = str:gsub("{3}", tostring(weapon_part or false)) + Print(str) + return end -end) -function ModShop:IsMaskOrModAllowed(mod, allowance_list) + local global_value = entry.infamous and "infamous" or entry.global_value or entry.dlc or entry.dlcs and entry.dlcs[math.random(#entry.dlcs)] or "normal" + local purchase_data = { + name = data.name, + name_localized = data.name_localized, + category = weapon_part and "weapon_mods" or data.category, + is_weapon_part = weapon_part, + bitmap_texture = data.bitmap_texture, + global_value = global_value, + tweak_data = entry, + price = 1, + } + purchase_data.price = self:GetItemPrice( purchase_data ) - if mod == nil then + if self:IsItemExluded( purchase_data.name ) then return false end - local gv = mod.global_value - if gv == nil then - return true + if self:IsGlobalValueDLC( purchase_data.global_value ) and not managers.dlc:is_dlc_unlocked( purchase_data.global_value ) then + return false end - if ModShop.ExclusionList[mod.name] == true or ModShop.ExclusionList[gv] == true then + if self:IsInfamyLocked( purchase_data ) then return false end for k, v in pairs( tweak_data.dlc ) do if v.achievement_id ~= nil and v.content ~= nil and v.content.loot_drops ~= nil then for i, loot in pairs( v.content.loot_drops ) do - if loot.item_entry ~= nil and loot.item_entry == mod.name then - return managers.achievment.handler:has_achievement(v.achievement_id) - end - end - end - end + if loot.item_entry ~= nil and loot.item_entry == purchase_data.name then - local infamy_lock = tweak_data.blackmarket[mod.category][mod.name].infamy_lock - if infamy_lock ~= nil or gv == "infamy" then - local is_unlocked = managers.infamy:owned(infamy_lock) or infamy_lock == nil - if not is_unlocked then - return false - end - end + if not managers.achievment.handler:has_achievement(v.achievement_id) then - if allowance_list and allowance_list[gv] then - return true - end - - if gv ~= "infamy" and not managers.dlc:is_dlc_unlocked(gv) then - return false - end + local achievement_tracker = tweak_data.achievement[ purchase_data.is_weapon_part and "weapon_part_tracker" or "mask_tracker" ] + local achievement_progress = achievement_tracker[purchase_data.name] + if achievement_progress then + return false + end - return true - -end - -Hooks:Add("BlackMarketManagerModifyGetInventoryCategory", "BlackMarketManagerModifyGetInventoryCategory_" .. Mod:ID(), function(blackmarket, category, data) + if not purchase_data.is_weapon_part then + return false + end + + end - for k, v in pairs( tweak_data.blackmarket[category] ) do - - local already_in_table = false - for x, y in pairs( data ) do - if y.id == k then - already_in_table = true - end - end - - local gv = v.dlc or v.global_value or "normal" - if not already_in_table and ModShop.ExclusionList[k] ~= true then - - if v.infamous then - gv = "infamous" - end - - if gv == "normal" or gv == "infamous" or gv == "infamy" or ( (gv ~= "normal" and managers.dlc:is_dlc_unlocked(gv)) or ModShop.DLCAlwaysUnlocked[gv] == true ) then - table.insert(data, { - id = k, - global_value = gv, - amount = 0 - }) + end end - end - end -end) - --- Purchase Hooks -Hooks:Add("ModShopAttemptPurchaseWeaponMod", "ModShopAttemptPurchaseWeaponMod_" .. Mod:ID(), function(data) - ModShop:SetWeaponModPurchaseData(data) - ModShop:ShowPurchaseMenu() -end) - -Hooks:Add("ModShopAttemptPurchaseMask", "ModShopAttemptPurchaseMask_" .. Mod:ID(), function(data) - ModShop:SetMaskPurchaseData(data) - ModShop:ShowPurchaseMenu() -end) - -Hooks:Add("ModShopAttemptPurchaseMaskPart", "ModShopAttemptPurchaseMaskPart_" .. Mod:ID(), function(data) - ModShop:SetMaskPartPurchaseData(data) - ModShop:ShowPurchaseMenu() -end) - --- Purchase Menu -function ModShop:SetPurchaseData( data ) - - self._purchase_data = {} - self._purchase_data.name = data.name - self._purchase_data.name_localized = data.name_localized - self._purchase_data.category = data.category - self._purchase_data.global_value = data.global_value - self._purchase_data.cost = ModShop.CostRegular - -end - -function ModShop:SetWeaponModPurchaseData( data ) - - if data then - - self:SetPurchaseData( data ) - - if self:IsWeaponMod( data.category ) then - if data.free_of_charge == true then - self._purchase_data.free_of_charge = true - end - end - + if purchase_data.tweak_data.is_a_unlockable then + return false end -end - -function ModShop:SetMaskPurchaseData( data ) + return true, purchase_data - if data then +end - self:SetPurchaseData( data ) +function ModShop:ShowItemPurchaseMenu( purchase_data ) - if self:IsMask( data.category ) then + local currency_name = GoonBase.ExtendedInventory:GetItem( ModShop.PurchaseCurrency ).name + local title = managers.localization:text("gm_gms_purchase_window_title") + local message = managers.localization:text("gm_gms_purchase_window_message") + message = message:gsub("{1}", purchase_data.name_localized) + message = message:gsub("{2}", tostring(purchase_data.price)) + message = message:gsub("{3}", managers.localization:text(currency_name) .. (purchase_data.price > 1 and "s" or "")) - local price = ModShop.MaskPricing[ data.global_value ] or ModShop.MaskPricing["default"] - if data.dlc then - price = ModShop.MaskPricing["dlc"] - end - if data.infamy_lock then - price = ModShop.MaskPricing["infamy"] - end + local dialog_data = {} + dialog_data.title = title + dialog_data.text = message + dialog_data.id = "gms_purchase_item_window" - self._purchase_data.cost = price + local ok_button = {} + ok_button.text = managers.localization:text("dialog_yes") + ok_button.callback_func = callback( self, self, "_PurchaseItem", purchase_data ) - end + local cancel_button = {} + cancel_button.text = managers.localization:text("dialog_no") + cancel_button.cancel_button = true - end + dialog_data.button_list = {ok_button, cancel_button} + dialog_data.purchase_data = purchase_data + managers.system_menu:show( dialog_data ) end -function ModShop:SetMaskPartPurchaseData( data ) - - if data then - - self:SetPurchaseData( data ) - - if self:IsMaskPart( data.category ) then - - local mod_data = tweak_data.blackmarket[data.category][data.name] - if mod_data then - - if mod_data.infamous == true then - self._purchase_data.cost = ModShop.CostInfamous - end +function ModShop:_PurchaseItem( purchase_data ) - if mod_data.global_value == "infamy" or mod_data.infamy_lock then - self._purchase_data.cost = ModShop.CostInfamous - end + if not purchase_data then + return + end - end + Print("Purchased item with gage-coins:\n\tItem name: " .. tostring(purchase_data.name) .. "\n\tCategory: " .. tostring(purchase_data.category)) + managers.blackmarket:add_to_inventory(purchase_data.global_value, purchase_data.category, purchase_data.name, true) - end + ExtendedInv:TakeItem( ModShop.PurchaseCurrency, purchase_data.price ) + self:_ReloadBlackMarket() + if Global.wallet_panel then + WalletGuiObject.refresh() end end -function ModShop:ShowPurchaseMenu() +-- Hooks +Hooks:Add("BlackMarketGUIPostSetup", "BlackMarketGUIPostSetup_" .. Mod:ID(), function(gui, is_start_page, component_data) - if not ExtendedInv then - Print("[Error] Attempting to show purchase menu with no Extended Inventory...") - return + gui.modshop_purchase_weaponmod_callback = function(self, data) + ModShop:AttemptItemPurchase( data, true ) end - local gage_coins = ExtendedInv:GetItem( ModShop.PurchaseCurrency ) - local purchase_cost = self._purchase_data.cost - - if not gage_coins.amount or type(gage_coins.amount) == "string" then - Print("[Error] Attempting to show purchase menu with no coin amount, or a string as the amount") - return - end - if not purchase_cost or type(purchase_cost) == "string" then - Print("[Error] Attmpting to show purchase menu with a string as the purchasing cost") - return + gui.modshop_purchase_mask_callback = function(self, data) + ModShop:AttemptItemPurchase( data ) end - -- Check if item is free of charge - if self._purchase_data.free_of_charge == true then - self:ShowFreeOfCharge() - return + gui.modshop_purchase_mask_part_callback = function(self, data) + ModShop:AttemptItemPurchase( data ) end - -- Check if we can afford this - if gage_coins.amount < purchase_cost or purchase_cost <= 0 then - self:ShowNotEnoughCoins( purchase_cost ) - return - end - - -- Show purchase menu - local title = managers.localization:text("gm_gms_purchase_window_title") - local message = managers.localization:text("gm_gms_purchase_window_message") - message = message:gsub("{1}", self._purchase_data.name_localized) - message = message:gsub("{2}", purchase_cost) - message = message:gsub("{3}", gage_coins.amount) - message = message:gsub("{4}", gage_coins.amount - purchase_cost) - - local menuOptions = {} - menuOptions[1] = { - text = managers.localization:text("gm_gms_purchase_window_accept"), - callback = ModShop.PurchaseItem - } - menuOptions[2] = { - text = managers.localization:text("gm_gms_purchase_window_cancel"), - callback = nil, - is_cancel_button = true + local wm_modshop = { + prio = 5, + btn = "BTN_BACK", + pc_btn = Idstring("toggle_chat"), + name = "gm_gms_purchase", + callback = callback(gui, gui, "modshop_purchase_weaponmod_callback") } - local window = QuickMenu:new(title, message, menuOptions, true) - -end - -function ModShop:ShowFreeOfCharge() - local title = managers.localization:text("gm_gms_purchase_failed") - local message = managers.localization:text("gm_gms_free_of_charge_message") - message = message:gsub("{1}", self._purchase_data.name_localized) - local menuOptions = {} - menuOptions[1] = { - text = managers.localization:text("gm_gms_free_of_charge_accept"), - is_cancel_button = true + local bm_modshop = { + prio = 5, + btn = "BTN_BACK", + pc_btn = Idstring("toggle_chat"), + name = "gm_gms_purchase", + callback = callback(gui, gui, "modshop_purchase_mask_callback") } - local window = QuickMenu:new(title, message, menuOptions, true) - -end - -function ModShop:ShowNotEnoughCoins(cost) - local title = managers.localization:text("gm_gms_purchase_failed") - local message = managers.localization:text("gm_gms_cannot_afford_message") - message = message:gsub("{1}", self._purchase_data.name_localized) - message = message:gsub("{2}", cost) - local menuOptions = {} - menuOptions[1] = { - text = managers.localization:text("gm_gms_cannot_afford_accept"), - is_cancel_button = true + local mp_modshop = { + prio = 5, + btn = "BTN_BACK", + pc_btn = Idstring("toggle_chat"), + name = "gm_gms_purchase", + callback = callback(gui, gui, "modshop_purchase_mask_part_callback") } - local window = QuickMenu:new(title, message, menuOptions, true) -end + local btn_x = 10 + gui._btns["wm_modshop"] = BlackMarketGuiButtonItem:new(gui._buttons, wm_modshop, btn_x) + gui._btns["bm_modshop"] = BlackMarketGuiButtonItem:new(gui._buttons, bm_modshop, btn_x) + gui._btns["mp_modshop"] = BlackMarketGuiButtonItem:new(gui._buttons, mp_modshop, btn_x) -function ModShop:PurchaseItem() +end) - if not ExtendedInv then - Print("[Error] Attempting to purchase item with no Extended Inventory...") - return - end - - local purchase_data = ModShop._purchase_data - local item = purchase_data.name - local category = purchase_data.category - local cost = purchase_data.cost - local global_value = purchase_data.global_value - - Print("Purchasing ", item, " from category ", category, " at cost: ", cost, " coins") - - -- Add to weapon inventory - if ModShop:IsWeaponMod(category) then - managers.blackmarket:add_to_inventory(global_value, "weapon_mods", item, true) - ModShop:ReloadBlackMarketAfterPurchase() +Hooks:Add("BlackMarketGUIOnPopulateModsActionList", "BlackMarketGUIOnPopulateModsActionList_" .. Mod:ID(), function(gui, data) + if ModShop:VerifyItemPurchase( data, true ) then + table.insert(data, "wm_modshop") end +end) - -- Add to mask inventory - if ModShop:IsMaskPart(category) then - - managers.blackmarket:add_traded_mask_part_to_inventory(item, category) - - -- Temporary measure to reload mask mods inventory - local blackmarket_gui = managers.menu_component._blackmarket_gui - if blackmarket_gui then - blackmarket_gui:_abort_customized_mask_callback() - end - +Hooks:Add("BlackMarketGUIOnPopulateBuyMasksActionList", "BlackMarketGUIOnPopulateBuyMasksActionList_" .. Mod:ID(), function(gui, data) + if ModShop:VerifyItemPurchase( data, false ) then + table.insert(data, "bm_modshop") end +end) - -- Add mask to inventory - if ModShop:IsMask(category) then - managers.blackmarket:add_to_inventory(global_value, "masks", item, true) - ModShop:ReloadBlackMarketAfterPurchase() +Hooks:Add("BlackMarketGUIOnPopulateMaskModsActionList", "BlackMarketGUIOnPopulateMaskModsActionList_" .. Mod:ID(), function(gui, data) + if ModShop:VerifyItemPurchase( data, false ) then + table.insert(data, "mp_modshop") end +end) - -- Remove coins - ExtendedInv:TakeItem( ModShop.PurchaseCurrency, cost ) +Hooks:Add("BlackMarketManagerModifyGetInventoryCategory", "BlackMarketManagerModifyGetInventoryCategory_" .. Mod:ID(), function(blackmarket, category, data) -end + local blackmarket_table = {} + for k, v in pairs( tweak_data.blackmarket[category] ) do -function ModShop:ReloadBlackMarketAfterPurchase() - local blackmarket_gui = managers.menu_component._blackmarket_gui - if blackmarket_gui then - blackmarket_gui:reload() - end -end + local already_in_table = blackmarket_table[v.id] + for x, y in pairs( data ) do + blackmarket_table[y.id] = true + if y.id == k then + already_in_table = true + end + end -function ModShop:IsWeaponMod(category) - if category == "primaries" or category == "secondaries" then - return true - end - return false -end + local global_value = v.infamous and "infamous" or v.global_value or v.dlc or v.dlcs and v.dlcs[math.random(#v.dlcs)] or "normal" + if not already_in_table and not ModShop:IsItemExluded(k) then + + local add_item = true + if ModShop:IsGlobalValueDLC( global_value ) and not managers.dlc:is_dlc_unlocked( global_value ) then + add_item = false + end -function ModShop:IsMask(category) - if category == "masks" then - return true - end - return false -end + if add_item then + local item_data = { + id = k, + global_value = global_value, + amount = 0 + } + table.insert(data, item_data) + end -function ModShop:IsMaskPart(category) - if category == "colors" or category == "textures" or category == "materials" then - return true + end + end - return false -end + +end) From 6a2e14e95f152e6ab46d0d8c40bee793108a6da1 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Wed, 18 Mar 2015 22:25:15 +1100 Subject: [PATCH 61/92] Removed obsolete train heist mod --- GoonMod/mods/disabled/train_heist_plans.lua | 111 -------------------- 1 file changed, 111 deletions(-) delete mode 100644 GoonMod/mods/disabled/train_heist_plans.lua diff --git a/GoonMod/mods/disabled/train_heist_plans.lua b/GoonMod/mods/disabled/train_heist_plans.lua deleted file mode 100644 index bbbd03c..0000000 --- a/GoonMod/mods/disabled/train_heist_plans.lua +++ /dev/null @@ -1,111 +0,0 @@ - --- Mod Definition -local Mod = class( BaseMod ) -Mod.id = "TrainHeistPlans" -Mod.Name = "Separate Train Heist" -Mod.Desc = "The train heist from the Armoured Transport DLC is available as a separate heist.\nWARNING: Will cause problems with people who do not have the mod." -Mod.Requirements = { "ExtendedInventory" } -Mod.Incompatibilities = {} - -Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() - GoonBase.Mods:RegisterMod( Mod ) -end) - -if not Mod:IsEnabled() then - return -end - -Hooks:Add("NarrativeTweakDataInit", "NarrativeTweakDataInit_" .. Mod:ID(), function(data) - - local ExtendedInv = _G.GoonBase.ExtendedInventory - if ExtendedInv == nil then - return - end - - if ExtendedInv:HasItem("train_heist_plans") then - - data.jobs.arm_for_prof = deep_clone( data.jobs.arm_for ) - data.jobs.arm_for_prof.contact = "bain" - data.jobs.arm_for_prof.professional = true - - table.insert( data._jobs_index, "arm_for_prof" ) - table.insert( data.jobs.arm_wrapper.job_wrapper, "arm_for_prof" ) - - data:set_job_wrappers() - - end - -end) - -Hooks:Add("LevelsTweakDataInit", "LevelsTweakDataInit_" .. Mod:ID(), function(data) - - local ExtendedInv = _G.GoonBase.ExtendedInventory - if ExtendedInv == nil then - return - end - - if ExtendedInv:HasItem("train_heist_plans") then - - data.arm_for_prof = deep_clone( data.arm_for ) - data.arm_for_prof.bonus_escape = false - data.arm_for_prof.static_experience = { - 60000, - 70000, - 80000, - 90000, - 100000 - } - - table.insert(data._level_index, "arm_for_prof") - - end - -end) - -Hooks:Add("ExtendedInventoryInitialized", "ExtendedInventoryInitialized_" .. Mod:ID(), function() - - local ExtendedInv = _G.GoonBase.ExtendedInventory - if ExtendedInv == nil then - return - end - - ExtendedInv:RegisterItem({ - id = "train_heist_plans", - name = "TrainHeist_PlansInv", - desc = "TrainHeist_PlansInvDesc", - texture = "guis/dlcs/dlc1/textures/pd2/mission_briefing/assets/train_01", - hide_when_none_in_stock = true, - }) - -end) - -Hooks:Add("JobManagerOnSetNextInteruptStage", "JobManagerOnSetNextInteruptStage_" .. Mod:ID(), function(job_manager, interupt) - - if interupt == "arm_for" then - - managers.job:set_next_interupt_stage(nil) - - local ExtendedInv = _G.GoonBase.ExtendedInventory - if ExtendedInv ~= nil then - ExtendedInv:AddItem("train_heist_plans", 1) - end - - end - -end) - -Hooks:Add("GameStateMachineChangeStateByName", "GameStateMachineChangeStateByName_" .. Mod:ID(), function(gsm, state_name, params) - - if state_name == "victoryscreen" then - - local level_id = Global.game_settings.level_id - if level_id == "arm_for" or level_id == "arm_for_prof" then - local ExtendedInv = _G.GoonBase.ExtendedInventory - if ExtendedInv ~= nil then - ExtendedInv:TakeItem("train_heist_plans", 1) - end - end - - end - -end) From ce0cc7ab8c290b113aec70ffa5b3071aa99ea21b Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Wed, 18 Mar 2015 23:35:51 +1100 Subject: [PATCH 62/92] Fixed failing to load customization for melee items while previewing melee weapons Added weapon visual customization options menu for clearing save file of visual blueprints --- GoonMod/menus/weapon_visual_customization.txt | 45 ++++++++ GoonMod/mods/weapon_customization.lua | 107 ++++++++---------- 2 files changed, 94 insertions(+), 58 deletions(-) create mode 100644 GoonMod/menus/weapon_visual_customization.txt diff --git a/GoonMod/menus/weapon_visual_customization.txt b/GoonMod/menus/weapon_visual_customization.txt new file mode 100644 index 0000000..6f6dbb2 --- /dev/null +++ b/GoonMod/menus/weapon_visual_customization.txt @@ -0,0 +1,45 @@ +{ + "menu_id" : "gm_options_weapon_customization_menu", + "parent_menu_id" : "goonbase_options_menu", + "title" : "gm_options_wc_title", + "description" : "gm_options_wc_desc", + "back_callback" : "ClosedGoonModOptions", + "items" : [ + + { + "type" : "button", + "id" : "gm_wc_clear_customization", + "title" : "gm_options_wc_clear_all_data_title", + "description" : "gm_options_wc_clear_all_data_desc", + "callback" : "WeaponCustomizationClearDataAll", + }, + { + "type" : "divider", + "size" : 16, + }, + + { + "type" : "button", + "id" : "gm_wc_clear_customization_primary", + "title" : "gm_options_wc_clear_primary_data_title", + "description" : "gm_options_wc_clear_primary_data_desc", + "callback" : "WeaponCustomizationClearDataPrimaries", + }, + { + "type" : "button", + "id" : "gm_wc_clear_customization_secondary", + "title" : "gm_options_wc_clear_secondary_data_title", + "description" : "gm_options_wc_clear_secondary_data_desc", + "callback" : "WeaponCustomizationClearDataSecondaries", + }, + { + "type" : "button", + "id" : "gm_wc_clear_customization_melee", + "title" : "gm_options_wc_clear_melee_data_title", + "description" : "gm_options_wc_clear_melee_data_desc", + "callback" : "WeaponCustomizationClearDataMelee", + } + + ] + +} diff --git a/GoonMod/mods/weapon_customization.lua b/GoonMod/mods/weapon_customization.lua index fa2ca0a..327d74e 100644 --- a/GoonMod/mods/weapon_customization.lua +++ b/GoonMod/mods/weapon_customization.lua @@ -18,7 +18,7 @@ end -- Weapon Customization GoonBase.WeaponCustomization = GoonBase.WeaponCustomization or {} local WeaponCustomization = GoonBase.WeaponCustomization -WeaponCustomization.MenuId = "goonbase_weapon_customization_menu" +WeaponCustomization.MenuFile = "weapon_visual_customization.txt" WeaponCustomization._update_queue = {} WeaponCustomization._melee_save_path = SavePath .. "goonmod_weapon_customization_melee.txt" @@ -51,65 +51,27 @@ if GoonBase.Options.WeaponCustomization == nil then end -- Menu -Hooks:Add("MenuManagerSetupCustomMenus", "MenuManagerSetupCustomMenus_" .. Mod:ID(), function(menu_manager, menu_nodes) - MenuHelper:NewMenu( WeaponCustomization.MenuId ) -end) - -Hooks:Add("MenuManagerSetupGoonBaseMenu", "MenuManagerSetupGoonBaseMenu_" .. Mod:ID(), function(menu_manager, menu_nodes) - - -- Submenu Button - MenuHelper:AddButton({ - id = "weapon_customization_menu_button", - title = "Options_WeaponCustomizationName", - desc = "Options_WeaponCustomizationDesc", - next_node = WeaponCustomization.MenuId, - menu_id = "goonbase_options_menu", - }) +Hooks:Add( "MenuManagerInitialize", "MenuManagerInitialize_" .. Mod:ID(), function( menu_manager ) - -- Menu - MenuCallbackHandler.wc_download_mod_overrides = function(this, item) - if SystemInfo:platform() == Idstring("WIN32") then - os.execute( "explorer " .. WeaponCustomization._mod_overrides_download_location ) - end + -- Callbacks + MenuCallbackHandler.WeaponCustomizationClearDataAll = function(this, item) + WeaponCustomization:ClearDataFromSave() end - MenuCallbackHandler.clear_weapon_visual_customizations = function(this, item) - WeaponCustomization:ShowClearDataConfirmation() + MenuCallbackHandler.WeaponCustomizationClearDataPrimaries = function(this, item) + WeaponCustomization:EraseSaveDataPrimary() end - MenuHelper:AddButton({ - id = "weapon_customization_download_mod_overrides", - title = "WeaponCustomization_DownloadModOverridesManual", - desc = "WeaponCustomization_DownloadModOverridesManualDesc", - callback = "wc_download_mod_overrides", - menu_id = WeaponCustomization.MenuId, - priority = 100, - }) - - MenuHelper:AddDivider({ - id = "weapon_customization_divider1", - menu_id = WeaponCustomization.MenuId, - size = 16, - priority = 99, - }) + MenuCallbackHandler.WeaponCustomizationClearDataSecondaries = function(this, item) + WeaponCustomization:EraseSaveDataSecondary() + end - MenuHelper:AddButton({ - id = "weapon_customization_clear_data", - title = "WeaponCustomization_ClearDataButton", - desc = "WeaponCustomization_ClearDataButtonDesc", - callback = "clear_weapon_visual_customizations", - menu_id = WeaponCustomization.MenuId, - priority = 98, - }) + MenuCallbackHandler.WeaponCustomizationClearDataMelee = function(this, item) + WeaponCustomization:EraseSaveDataMelee() + end -end) + MenuHelper:LoadFromJsonFile( GoonBase.MenusPath .. WeaponCustomization.MenuFile, GoonBase.WeaponCustomization, GoonBase.Options.WeaponCustomization ) -Hooks:Add("MenuManagerBuildCustomMenus", "MenuManagerBuildCustomMenus_" .. Mod:ID(), function(menu_manager, mainmenu_nodes) - local menu_id = WeaponCustomization.MenuId - local data = { - area_bg = "none" - } - mainmenu_nodes[menu_id] = MenuHelper:BuildMenu( menu_id, data ) end) -- Hooks @@ -135,8 +97,18 @@ Hooks:Add("MenuSceneManagerSpawnedItemWeapon", "MenuSceneManagerSpawnedItemWeapo end) Hooks:Add("MenuSceneManagerSpawnedMeleeWeapon", "MenuSceneManagerSpawnedMeleeWeapon_" .. Mod:ID(), function(menu, melee_weapon_id, spawned_unit) + WeaponCustomization._menu_weapon_preview_unit = spawned_unit - WeaponCustomization:_open_weapon_customization_preview_node() + WeaponCustomization:LoadCurrentWeaponCustomization({ + name = melee_weapon_id, + category = "melee_weapons", + slot = 0 + }) + + if managers.blackmarket._customizing_weapon_data then + WeaponCustomization:_open_weapon_customization_preview_node() + end + end) Hooks:Add("NewRaycastWeaponBasePostAssemblyComplete", "NewRaycastWeaponBasePostAssemblyComplete_WeaponCustomization", function(weapon, clbk, parts, blueprint) @@ -742,7 +714,7 @@ function WeaponCustomization:ShowClearDataConfirmation() local menuOptions = {} menuOptions[1] = { text = managers.localization:text("WeaponCustomization_ClearDataAccept"), - callback = WeaponCustomization.ClearDataFromSave, + callback = callback(self, self, "ClearDataFromSave"), is_cancel_button = true } menuOptions[2] = { @@ -753,29 +725,48 @@ function WeaponCustomization:ShowClearDataConfirmation() end -function WeaponCustomization.ClearDataFromSave() +function WeaponCustomization:ClearDataFromSave() if not managers.blackmarket then return end - -- Erase primary weapons + self:EraseSaveDataPrimary() + self:EraseSaveDataSecondary() + self:EraseSaveDataMelee() + +end + +function WeaponCustomization:EraseSaveDataPrimary() + + Print(" Clearing weapon customization data from primary weapons") for k, v in pairs( managers.blackmarket._global.crafted_items["primaries"] ) do if v.visual_blueprint then + log("\tClearing visual blueprint: " .. tostring(v.weapon_id)) v.visual_blueprint = nil end end - -- Erase secondary weapons +end + +function WeaponCustomization:EraseSaveDataSecondary() + + Print(" Clearing weapon customization data from secondary weapons") for k, v in pairs( managers.blackmarket._global.crafted_items["secondaries"] ) do if v.visual_blueprint then + log("\tClearing visual blueprint: " .. tostring(v.weapon_id)) v.visual_blueprint = nil end end - -- Erase melee weapons +end + +function WeaponCustomization:EraseSaveDataMelee() + + Print(" Clearing weapon customization data from melee weapons") for k, v in pairs( managers.blackmarket._global["melee_weapons"] ) do if v.visual_blueprint then + log("\tClearing visual blueprint: " .. tostring(k)) v.visual_blueprint = nil end end From 4a4a19d78fce1a61db54a47fffef23f33889c981 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Wed, 18 Mar 2015 23:38:05 +1100 Subject: [PATCH 63/92] Removed old temporary mod override checks code Temporary weapon customizer data will be cleared properly when exiting the customizer Added new customizer localization keys --- GoonMod/loc/en.txt | 21 +++-- GoonMod/mods/weapon_customization_menus.lua | 96 ++++----------------- 2 files changed, 34 insertions(+), 83 deletions(-) diff --git a/GoonMod/loc/en.txt b/GoonMod/loc/en.txt index 4be4b80..c72c5e7 100644 --- a/GoonMod/loc/en.txt +++ b/GoonMod/loc/en.txt @@ -195,12 +195,21 @@ "gm_options_normalized_sensitivity_title" : "Enabled Ironsight Normalized Sensitivity", "gm_options_normalized_sensitivity_desc" : "Lower the sensitivity when using ironsights to more accurately place your shots.", - "Options_WeaponCustomizationName" : "Weapon Customization", - "Options_WeaponCustomizationDesc" : "Weapon Customization Options", - "WeaponCustomization_DownloadModOverridesManual" : "Download Mod Overrides (Manual Install)", - "WeaponCustomization_DownloadModOverridesManualDesc" : "Download the required mod overrides to use the Weapon Customization mod, opens in your browser.\nPlace the 'GoonModWeaponCustomizer' folder into your mod_overrides and then restart your game.", - "WeaponCustomization_ClearDataButton" : "Clear Weapon Customization Data", - "WeaponCustomization_ClearDataButtonDesc" : "Erase all of your weapon customization data from your Payday 2 save file", + "gm_options_wc_title" : "Weapon Visual Customization", + "gm_options_wc_desc" : "Manage your weapon visual customization data", + + "gm_options_wc_clear_all_data_title" : "Clear All Visual Customization Data", + "gm_options_wc_clear_all_data_desc" : "Erase all of your weapon visual customization data from your Payday 2 save file", + + "gm_options_wc_clear_primary_data_title" : "Clear Primary Weapons Customization", + "gm_options_wc_clear_primary_data_desc" : "Erase all of your customization data from all of your primary weapons", + + "gm_options_wc_clear_secondary_data_title" : "Clear Secondary Weapons Customization", + "gm_options_wc_clear_secondary_data_desc" : "Erase all of your customization data from all of your secondary weapons", + + "gm_options_wc_clear_melee_data_title" : "Clear Melee Weapons Customization", + "gm_options_wc_clear_melee_data_desc" : "Erase all of your customization data from all of your melee weapons", + "WeaponCustomization_ClearDataTitle" : "Clear Weapon Customization Data", "WeaponCustomization_ClearDataMessage" : "This will erase all weapon customization data from your save game.\nYou will lose all of your modified weapon visuals, but you will not lose the weapons themselves.\n\nYou may need to restart your game, or load a mission, to fully clear your texture cache.", "WeaponCustomization_ClearDataAccept" : "Clear Data", diff --git a/GoonMod/mods/weapon_customization_menus.lua b/GoonMod/mods/weapon_customization_menus.lua index 7be041c..bb0a2ca 100644 --- a/GoonMod/mods/weapon_customization_menus.lua +++ b/GoonMod/mods/weapon_customization_menus.lua @@ -480,6 +480,7 @@ Hooks:Add("MenuUpdate", "MenuUpdate_WeaponCustomization", function(t, dt) end) Hooks:Add("BlackMarketGUIOnPopulateWeapons", "BlackMarketGUIOnPopulateWeapons_WeaponCustomization", function(gui, category, data) + managers.blackmarket._customizing_weapon_data = nil WeaponCustomization:RestoreMenuColourGrading() end) @@ -610,7 +611,7 @@ function WeaponCustomization:LeftMouseReleased_Advanced(gui, button, x, y) local adv_option = self._advanced_menu_options[ line ] if adv_option and adv_option.func then - self[ adv_option.func ]() + self[ adv_option.func ](self) managers.menu_component:post_event("menu_enter") end @@ -753,8 +754,7 @@ function WeaponCustomization:AdvancedClearWeaponCheck() local menuOptions = {} menuOptions[1] = { text = managers.localization:text("wc_clear_weapon_accept"), - callback = WeaponCustomization.AdvancedClearWeaponAccept, - is_cancel_button = true + callback = callback(self, self, "_AdvancedClearWeaponAccept") } menuOptions[2] = { text = managers.localization:text("wc_clear_weapon_cancel"), @@ -764,29 +764,29 @@ function WeaponCustomization:AdvancedClearWeaponCheck() end -function WeaponCustomization.AdvancedClearWeaponAccept() +function WeaponCustomization:_AdvancedClearWeaponAccept() if managers.blackmarket._customizing_weapon_data then + return + end - local category = managers.blackmarket._customizing_weapon_data.category - local weapon = WeaponCustomization:GetWeaponTableFromInventory( managers.blackmarket._customizing_weapon_data ) + local category = managers.blackmarket._customizing_weapon_data.category + local weapon = self:GetWeaponTableFromInventory( managers.blackmarket._customizing_weapon_data ) - -- Rebuild weapon parts list - if category ~= "melee_weapons" then - WeaponCustomization:CreateCustomizablePartsList( weapon ) - end + -- Rebuild weapon parts list + if category ~= "melee_weapons" then + self:CreateCustomizablePartsList( weapon ) + end - -- Clear selected parts - managers.blackmarket._selected_weapon_parts = clone( WeaponCustomization._default_part_visual_blueprint ) + -- Clear selected parts + managers.blackmarket._selected_weapon_parts = clone( WeaponCustomization._default_part_visual_blueprint ) - -- Save - WeaponCustomization:UpdateWeaponPartsWithMod( nil, nil, managers.blackmarket._customizing_weapon_parts, category ~= "melee_weapons" ) + -- Save + self:UpdateWeaponPartsWithMod( nil, nil, managers.blackmarket._customizing_weapon_parts, category ~= "melee_weapons" ) - -- Clear data on slot - if weapon.visual_blueprint then - weapon.visual_blueprint = nil - end - + -- Clear data on slot + if weapon.visual_blueprint then + weapon.visual_blueprint = nil end end @@ -816,61 +816,3 @@ function WeaponCustomization:ShowControllerAdvancedOptions() local menu = QuickMenu:new(title, message, menuOptions, true) end - --- Temporary Popup -function WeaponCustomization:Temp_CheckOverridesInstalled() - - local file_name = "assets/mod_overrides/GoonModWeaponCustomizer/units/payday2/weapons/wpn_fps_ass_74_pts/wpn_fps_ass_74_b_standard.material_config" - file_name = Application:base_path() .. file_name - - local f= io.open(file_name, "r") - if f ~= nil then - io.close(f) - else - if GoonBase.Options.WeaponCustomization and not GoonBase.Options.WeaponCustomization.TempShownOverridesNotInstalled then - WeaponCustomization:Temp_ShowOverridesNotInstalledWindow() - end - end - -end - -function WeaponCustomization:Temp_ShowOverridesNotInstalledWindow() - - local title = managers.localization:text("wc_mod_overrides_not_installed_title") - local message = managers.localization:text("wc_mod_overrides_not_installed_desc") - local menuOptions = {} - menuOptions[1] = { - text = managers.localization:text("wc_mod_overrides_not_installed_download"), - callback = WeaponCustomization.Temp_DownloadOverrides, - is_cancel_button = true - } - menuOptions[2] = { - text = managers.localization:text("wc_mod_overrides_not_installed_dont_show"), - callback = WeaponCustomization.Temp_DontShowInFuture, - is_cancel_button = true - } - menuOptions[3] = { - text = managers.localization:text("wc_mod_overrides_not_installed_cancel"), - is_cancel_button = true - } - local menu = QuickMenu:new(title, message, menuOptions, true) - -end - -function WeaponCustomization:Temp_DownloadOverrides() - - if SystemInfo:platform() == Idstring("WIN32") then - os.execute( "explorer " .. WeaponCustomization._mod_overrides_download_location ) - os.execute( "explorer " .. Application:base_path() .. "assets\\mod_overrides\\" ) - end - -end - -function WeaponCustomization:Temp_DontShowInFuture() - - if GoonBase.Options.WeaponCustomization then - GoonBase.Options.WeaponCustomization.TempShownOverridesNotInstalled = true - GoonBase.Options:Save() - end - -end From 32884b2d98b11de2fc7acd8f6ab80650a35d268d Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Wed, 18 Mar 2015 23:38:51 +1100 Subject: [PATCH 64/92] Updated options menu to display json menu items in alphabetical order --- GoonMod/mods/extended_inventory.lua | 16 ++++++++-------- GoonMod/mods/push_to_interact.lua | 1 - GoonMod/mods/zoom_sensitivity.lua | 3 ++- GoonMod/req/mods.lua | 3 +-- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/GoonMod/mods/extended_inventory.lua b/GoonMod/mods/extended_inventory.lua index ec375fd..f79c55e 100644 --- a/GoonMod/mods/extended_inventory.lua +++ b/GoonMod/mods/extended_inventory.lua @@ -474,20 +474,20 @@ Hooks:Add("MenuManagerSetupGoonBaseMenu", "MenuManagerSetupGoonBaseMenu_" .. Mod ExtendedInv:_ShowCodeRedeemWindow() end - MenuHelper:AddDivider({ - id = "gm_ex_inv_divider", - menu_id = "goonbase_options_menu", - size = 16, - priority = -99, - }) - MenuHelper:AddButton({ id = "gm_ex_inv_redeem_button", title = "gm_ex_inv_redeem_code", desc = "gm_ex_inv_redeem_code_desc", callback = "extended_inv_open_redeem_code", menu_id = "goonbase_options_menu", - priority = -100, + priority = 100, + }) + + MenuHelper:AddDivider({ + id = "gm_ex_inv_divider", + menu_id = "goonbase_options_menu", + size = 16, + priority = 0, }) end) diff --git a/GoonMod/mods/push_to_interact.lua b/GoonMod/mods/push_to_interact.lua index 923e8ff..e100ddc 100644 --- a/GoonMod/mods/push_to_interact.lua +++ b/GoonMod/mods/push_to_interact.lua @@ -66,7 +66,6 @@ Hooks:Add("MenuManagerInitialize", "MenuManagerInitialize_" .. Mod:ID(), functio end MenuHelper:LoadFromJsonFile( GoonBase.MenusPath .. PushToInteract.MenuFile, PushToInteract, GoonBase.Options.PushToInteract ) - end) -- Hooks diff --git a/GoonMod/mods/zoom_sensitivity.lua b/GoonMod/mods/zoom_sensitivity.lua index 80031a7..f289a5f 100644 --- a/GoonMod/mods/zoom_sensitivity.lua +++ b/GoonMod/mods/zoom_sensitivity.lua @@ -33,7 +33,8 @@ Hooks:Add("MenuManagerSetupGoonBaseMenu", "MenuManagerSetupGoonBaseMenu_" .. Mod desc = "gm_options_normalized_sensitivity_desc", callback = "toggle_zoom_sensitivity", value = GoonBase.Options.IronsightSensitivity.Enabled, - menu_id = "goonbase_options_menu" + menu_id = "goonbase_options_menu", + priority = 101, }) end) diff --git a/GoonMod/req/mods.lua b/GoonMod/req/mods.lua index c1b44db..5320ddd 100644 --- a/GoonMod/req/mods.lua +++ b/GoonMod/req/mods.lua @@ -321,9 +321,8 @@ function BaseMod:SetupMenu() desc = self:DescKey(), callback = menu_name, value = self:IsEnabled(), - disabled_color = Color( 0.8, 0.3, 0.3, 0.3 ), + disabled_color = Color( 1.0, 0.3, 0.3, 0.3 ), menu_id = Mods.MenuID, - priority = self.Priority or 0 }) end From 0751f0525a23aed18b73c18dbdef27b66b645d3b Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Thu, 19 Mar 2015 01:42:02 +1100 Subject: [PATCH 65/92] Mod loader can load from multiple defined mods folders --- GoonMod/goonbase.lua | 6 ++++-- GoonMod/req/mods.lua | 10 ++++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/GoonMod/goonbase.lua b/GoonMod/goonbase.lua index bda3e07..14f168d 100644 --- a/GoonMod/goonbase.lua +++ b/GoonMod/goonbase.lua @@ -10,7 +10,10 @@ if not _G.GoonBase then GoonBase.Path = "" GoonBase.LuaPath = "lua/" GoonBase.RequiresFolder = "req/" - GoonBase.ModsFolder = "mods/" + GoonBase.ModsFolders = { + "mods/", + "mods/custom_colours/" + } GoonBase.MenusPath = "menus/" GoonBase.LocalizationFolder = "loc/" @@ -148,7 +151,6 @@ if not GoonBase.HasLoadedScripts then GoonBase.LuaPath = ModPath .. GoonBase.LuaPath GoonBase.RequiresFolder = ModPath .. GoonBase.RequiresFolder - GoonBase.ModsFolder = ModPath .. GoonBase.ModsFolder GoonBase.MenusPath = ModPath .. GoonBase.MenusPath GoonBase.LocalizationFolder = ModPath .. GoonBase.LocalizationFolder diff --git a/GoonMod/req/mods.lua b/GoonMod/req/mods.lua index 5320ddd..9d62adb 100644 --- a/GoonMod/req/mods.lua +++ b/GoonMod/req/mods.lua @@ -97,10 +97,16 @@ function Mods:LoadMods() if GoonBase.SupportedVersion then - GoonBase.ModFiles = file.GetFiles( GoonBase.ModsFolder ) + GoonBase.ModFiles = {} + for k, v in pairs( GoonBase.ModsFolders ) do + local path = GoonBase.Path .. v + for x, y in pairs( file.GetFiles(path) ) do + table.insert( GoonBase.ModFiles, path .. y ) + end + end for k, v in pairs( GoonBase.ModFiles ) do - SafeDoFile( GoonBase.ModsFolder .. v ) + SafeDoFile( v ) end end From c93eea389aa4cc6bfc9f2d1c4d435512a951e09b Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Thu, 19 Mar 2015 01:43:28 +1100 Subject: [PATCH 66/92] Added ColourHSVRGB helper class --- GoonMod/mods/custom_colours/color_hsvrgb.lua | 96 ++++++++++++++++++++ GoonMod/req/options.lua | 2 +- 2 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 GoonMod/mods/custom_colours/color_hsvrgb.lua diff --git a/GoonMod/mods/custom_colours/color_hsvrgb.lua b/GoonMod/mods/custom_colours/color_hsvrgb.lua new file mode 100644 index 0000000..ea6226f --- /dev/null +++ b/GoonMod/mods/custom_colours/color_hsvrgb.lua @@ -0,0 +1,96 @@ + +ColorHSVRGB = class() + +local KeyEnabled = "Enabled" +local KeyRedHue = "RH" +local KeyGreenSat = "GS" +local KeyBlueValue = "BV" +local KeyUseHSV = "UseHSV" + +function ColorHSVRGB:init( options_table, default_color ) + self._options = options_table or {} + self._default = default_color +end + +function ColorHSVRGB:IsEnabled() + return self._options[KeyEnabled] +end + +function ColorHSVRGB:GetRedHue() + return self._options[KeyRedHue] +end + +function ColorHSVRGB:GetGreenSaturation() + return self._options[KeyGreenSat] +end + +function ColorHSVRGB:GetBlueValue() + return self._options[KeyBlueValue] +end + +function ColorHSVRGB:IsUsingHSV() + return self._options[KeyUseHSV] +end + +function ColorHSVRGB:GetColor(alpha) + + if not self:IsEnabled() then + return self._default + end + + local r, g, b = self:GetRedHue(), self:GetGreenSaturation(), self:GetBlueValue() + if self:IsUsingHSV() then + r, g, b = self:ToRGB(r, g, b) + end + + return Color(alpha or 1, r, g, b) + +end + +function ColorHSVRGB:GetRainbowColor( time, speed ) + t = t or 1 + speed = speed or 1 + local r, g, b = self:ToRGB( math.sin((speed * time) % 80), self:GetGreenSaturation(), self:GetBlueValue() ) + return Color( r, g, b ) +end + +function ColorHSVRGB:ToRGB(h, s, v) + + local r, g, b + + local i = math.floor(h * 6) + local f = h * 6 - i + local p = v * (1 - s) + local q = v * (1 - f * s) + local t = v * (1 - (1 - f) * s) + + local mod = i % 6 + if mod == 0 then + r = v + g = t + b = p + elseif mod == 1 then + r = q + g = v + b = p + elseif mod == 2 then + r = p + g = v + b = t + elseif mod == 3 then + r = p + g = q + b = v + elseif mod == 4 then + r = t + g = p + b = v + elseif mod == 5 then + r = v + g = p + b = q + end + + return r, g, b + +end diff --git a/GoonMod/req/options.lua b/GoonMod/req/options.lua index f7ef825..3d6a76a 100644 --- a/GoonMod/req/options.lua +++ b/GoonMod/req/options.lua @@ -43,7 +43,7 @@ Hooks:Add("MenuManagerBuildCustomMenus", "MenuManagerBuildCustomMenus_OptionsMen local mod_options_menu = LuaModManager.Constants._lua_mod_options_menu_id if mainmenu_nodes[mod_options_menu] then - mainmenu_nodes[options_menu_id] = MenuHelper:BuildMenu( options_menu_id ) + mainmenu_nodes[options_menu_id] = MenuHelper:BuildMenu( options_menu_id ) MenuHelper:AddMenuItem( mainmenu_nodes[mod_options_menu], options_menu_id, "gm_options_menu", "gm_options_menu_desc" ) Hooks:Call( "MenuManagerPostSetupGoonBaseMenu", menu_manager, mainmenu_nodes ) From e17dd4f4d486fda8dedd9d40574d86cba8b27bcf Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Thu, 19 Mar 2015 01:45:59 +1100 Subject: [PATCH 67/92] Converted custom weapon flashlight and world laser colours to BLT Custom colour menu nodes now use json-loaded menu files Custom colour menu nodes have a colour sample space that updates in realtime --- GoonMod/loc/en.txt | 47 ++-- GoonMod/menus/custom_flashlight_menu.txt | 99 ++++++++ GoonMod/menus/custom_world_laser_menu.txt | 99 ++++++++ .../mods/custom_colours/weapon_flashlight.lua | 213 +++++++++++++++++ .../custom_colours/world_laser_colors.lua | 211 +++++++++++++++++ GoonMod/mods/disabled/colors/color_hsvrgb.lua | 216 ------------------ .../disabled/colors/weapon_flashlight.lua | 194 ---------------- .../disabled/colors/world_laser_colors.lua | 165 ------------- 8 files changed, 645 insertions(+), 599 deletions(-) create mode 100644 GoonMod/menus/custom_flashlight_menu.txt create mode 100644 GoonMod/menus/custom_world_laser_menu.txt create mode 100644 GoonMod/mods/custom_colours/weapon_flashlight.lua create mode 100644 GoonMod/mods/custom_colours/world_laser_colors.lua delete mode 100644 GoonMod/mods/disabled/colors/color_hsvrgb.lua delete mode 100644 GoonMod/mods/disabled/colors/weapon_flashlight.lua delete mode 100644 GoonMod/mods/disabled/colors/world_laser_colors.lua diff --git a/GoonMod/loc/en.txt b/GoonMod/loc/en.txt index c72c5e7..ebe9519 100644 --- a/GoonMod/loc/en.txt +++ b/GoonMod/loc/en.txt @@ -210,13 +210,6 @@ "gm_options_wc_clear_melee_data_title" : "Clear Melee Weapons Customization", "gm_options_wc_clear_melee_data_desc" : "Erase all of your customization data from all of your melee weapons", - "WeaponCustomization_ClearDataTitle" : "Clear Weapon Customization Data", - "WeaponCustomization_ClearDataMessage" : "This will erase all weapon customization data from your save game.\nYou will lose all of your modified weapon visuals, but you will not lose the weapons themselves.\n\nYou may need to restart your game, or load a mission, to fully clear your texture cache.", - "WeaponCustomization_ClearDataAccept" : "Clear Data", - "WeaponCustomization_ClearDataCancel" : "Cancel", - "WeaponCustomization_PrintAllPartNames" : "Output All Weapon Part Names", - "WeaponCustomization_PrintAllPartNamesDesc" : "Outputs all weapon part names to a CSV file", - "bm_mtl_no_material" : "No Material", "WeaponCustomization_MenuItem" : "Customize Weapon", "bm_menu_customize_weapon_title" : "Customize Weapon: $weapon_name", @@ -248,14 +241,29 @@ "Options_EnemyLaserRainbowSpeedTitle" : "Rainbow Speed", "Options_EnemyLaserRainbowSpeedDesc" : "Set the speed of the rainbow effect", - "Options_WeaponLightName" : "Weapon Flashlight Color", - "Options_WeaponLightDesc" : "Modify the color of weapon flashlights", - "Options_WeaponLightEnableTitle" : "Enable Custom Weapon Flashlight Color", - "Options_WeaponLightEnableDesc" : "Use the custom set color for Weapon Flashlights", - "Options_WeaponLightRainbowTitle" : "Enable Rainbow Light", - "Options_WeaponLightRainbowDesc" : "Enable rainbow instead of the set Hue", - "Options_WeaponLightRainbowSpeedTitle" : "Rainbow Speed", - "Options_WeaponLightRainbowSpeedDesc" : "Set the speed of the rainbow effect", + "gm_options_custom_use_hue_title" : "Use HSV Colour Space", + "gm_options_custom_use_hue_desc" : "Use the HSV colour space instead of the RGB", + "gm_options_custom_rh_title" : "Red/Hue", + "gm_options_custom_rh_desc" : "The amount of red in, or the hue of the colour", + "gm_options_custom_gs_title" : "Green/Saturation", + "gm_options_custom_gs_desc" : "The amount of green in, or the saturation of the colour", + "gm_options_custom_bv_title" : "Blue/Value", + "gm_options_custom_bv_desc" : "The amount of blue in, or the value of the colour", + + "gm_options_custom_rainbow_title" : "Enable Rainbow Effect", + "gm_options_custom_rainbow_desc" : "Enable cycling rainbow colour effect", + "gm_options_custom_rainbow_speed_title" : "Rainbow Effect Speed", + "gm_options_custom_rainbow_speed_desc" : "Sets the speed of the rainbow effect", + + "gm_options_cfc_menu_title" : "Weapon Flashlight Color", + "gm_options_cfc_menu_desc" : "Modify the color of weapon flashlights", + "gm_options_cfc_enabled_title" : "Enable Custom Weapon Flashlight Color", + "gm_options_cfc_enabled_desc" : "Use the custom set color for Weapon Flashlights", + + "gm_options_cwl_menu_title" : "World Laser Colour", + "gm_options_cwl_menu_desc" : "Modify the color of lasers that appear in the world like on mines and sensors", + "gm_options_cwl_enabled_title" : "Enable Custom World Laser Colour", + "gm_options_cwl_enabled_desc" : "Use the custom set color for lasers in the world", "Options_WeaponLaserName" : "Weapon Laser Color", "Options_WeaponLaserDesc" : "Modify the color of weapon lasers", @@ -271,13 +279,4 @@ "Options_TeammateLaser_Theirs" : "Use Their Colour", "Options_TeammateLaser_Unique" : "Unique per Person", - "Options_WorldLaserName" : "World Laser Color", - "Options_WorldLaserDesc" : "Modify the color of lasers that appear in the world", - "Options_WorldLaserEnableTitle" : "Enable Custom World Laser Color", - "Options_WorldLaserEnableDesc" : "Use the custom set color for World Lasers", - "Options_WorldLaserRainbowTitle" : "Enable Rainbow Laser", - "Options_WorldLaserRainbowDesc" : "Enable rainbow instead of the set Hue", - "Options_WorldLaserRainbowSpeedTitle" : "Rainbow Speed", - "Options_WorldLaserRainbowSpeedDesc" : "Set the speed of the rainbow effect", - } diff --git a/GoonMod/menus/custom_flashlight_menu.txt b/GoonMod/menus/custom_flashlight_menu.txt new file mode 100644 index 0000000..b2691f4 --- /dev/null +++ b/GoonMod/menus/custom_flashlight_menu.txt @@ -0,0 +1,99 @@ +{ + "menu_id" : "gm_options_cfc_menu", + "parent_menu_id" : "goonbase_options_menu", + "title" : "gm_options_cfc_menu_title", + "description" : "gm_options_cfc_menu_desc", + "focus_changed_callback" : "CustomFlashlightMenuChangeFocus", + "back_callback" : "ClosedGoonModOptions", + "area_bg" : "half", + "items" : [ + + { + "type" : "toggle", + "id" : "gm_cfc_toggle_custom_flashlight", + "title" : "gm_options_cfc_enabled_title", + "description" : "gm_options_cfc_enabled_desc", + "callback" : "ToggleEnableCustomFlashlight", + "value" : "Enabled", + "default_value" : true, + }, + { + "type" : "divider", + "size" : 8, + }, + + { + "type" : "toggle", + "id" : "gm_cfc_toggle_custom_flashlight_use_hue", + "title" : "gm_options_custom_use_hue_title", + "description" : "gm_options_custom_use_hue_desc", + "callback" : "CustomFlashlightToggleUseHue", + "value" : "UseHSV", + "default_value" : false, + }, + { + "type" : "slider", + "id" : "gm_cfc_colour_slider_rh", + "title" : "gm_options_custom_rh_title", + "description" : "gm_options_custom_rh_desc", + "callback" : "CustomFlashlightSetRedHue", + "value" : "RH", + "default_value" : 1, + "min" : 0, + "max" : 1, + "step" : 0.01, + }, + { + "type" : "slider", + "id" : "gm_cfc_colour_slider_gs", + "title" : "gm_options_custom_gs_title", + "description" : "gm_options_custom_gs_desc", + "callback" : "CustomFlashlightSetGreenSaturation", + "value" : "GS", + "default_value" : 1, + "min" : 0, + "max" : 1, + "step" : 0.01, + }, + { + "type" : "slider", + "id" : "gm_cfc_colour_slider_bv", + "title" : "gm_options_custom_bv_title", + "description" : "gm_options_custom_bv_desc", + "callback" : "CustomFlashlightSetBlueValue", + "value" : "BV", + "default_value" : 1, + "min" : 0, + "max" : 1, + "step" : 0.01, + }, + { + "type" : "divider", + "size" : 64, + }, + + { + "type" : "toggle", + "id" : "gm_cfc_toggle_rainbow", + "title" : "gm_options_custom_rainbow_title", + "description" : "gm_options_custom_rainbow_desc", + "callback" : "CustomFlashlightSetUseRainbow", + "value" : "UseRainbow", + "default_value" : false, + }, + { + "type" : "slider", + "id" : "gm_cfc_slider_rainbow_speed", + "title" : "gm_options_custom_rainbow_speed_title", + "description" : "gm_options_custom_rainbow_speed_desc", + "callback" : "CustomFlashlightSetRainbowSpeed", + "value" : "RainbowSpeed", + "default_value" : 1, + "min" : 1, + "max" : 100, + "step" : 1, + } + + ] + +} diff --git a/GoonMod/menus/custom_world_laser_menu.txt b/GoonMod/menus/custom_world_laser_menu.txt new file mode 100644 index 0000000..0cb12f9 --- /dev/null +++ b/GoonMod/menus/custom_world_laser_menu.txt @@ -0,0 +1,99 @@ +{ + "menu_id" : "gm_options_cwl_menu", + "parent_menu_id" : "goonbase_options_menu", + "title" : "gm_options_cwl_menu_title", + "description" : "gm_options_cwl_menu_desc", + "focus_changed_callback" : "CustomWorldLaserMenuChangeFocus", + "back_callback" : "ClosedGoonModOptions", + "area_bg" : "half", + "items" : [ + + { + "type" : "toggle", + "id" : "gm_cwl_toggle_custom_laser", + "title" : "gm_options_cwl_enabled_title", + "description" : "gm_options_cwl_enabled_desc", + "callback" : "ToggleEnableCustomWorldLaser", + "value" : "Enabled", + "default_value" : true, + }, + { + "type" : "divider", + "size" : 8, + }, + + { + "type" : "toggle", + "id" : "gm_cwl_toggle_custom_use_hue", + "title" : "gm_options_custom_use_hue_title", + "description" : "gm_options_custom_use_hue_desc", + "callback" : "CustomWorldLaserToggleUseHue", + "value" : "UseHSV", + "default_value" : false, + }, + { + "type" : "slider", + "id" : "gm_cwl_colour_slider_rh", + "title" : "gm_options_custom_rh_title", + "description" : "gm_options_custom_rh_desc", + "callback" : "CustomWorldLaserSetRedHue", + "value" : "RH", + "default_value" : 1, + "min" : 0, + "max" : 1, + "step" : 0.01, + }, + { + "type" : "slider", + "id" : "gm_cwl_colour_slider_gs", + "title" : "gm_options_custom_gs_title", + "description" : "gm_options_custom_gs_desc", + "callback" : "CustomWorldLaserSetGreenSaturation", + "value" : "GS", + "default_value" : 1, + "min" : 0, + "max" : 1, + "step" : 0.01, + }, + { + "type" : "slider", + "id" : "gm_cwl_colour_slider_bv", + "title" : "gm_options_custom_bv_title", + "description" : "gm_options_custom_bv_desc", + "callback" : "CustomWorldLaserSetBlueValue", + "value" : "BV", + "default_value" : 1, + "min" : 0, + "max" : 1, + "step" : 0.01, + }, + { + "type" : "divider", + "size" : 64, + }, + + { + "type" : "toggle", + "id" : "gm_cwl_toggle_rainbow", + "title" : "gm_options_custom_rainbow_title", + "description" : "gm_options_custom_rainbow_desc", + "callback" : "CustomWorldLaserSetUseRainbow", + "value" : "UseRainbow", + "default_value" : false, + }, + { + "type" : "slider", + "id" : "gm_cwl_slider_rainbow_speed", + "title" : "gm_options_custom_rainbow_speed_title", + "description" : "gm_options_custom_rainbow_speed_desc", + "callback" : "CustomWorldLaserSetRainbowSpeed", + "value" : "RainbowSpeed", + "default_value" : 1, + "min" : 1, + "max" : 100, + "step" : 1, + } + + ] + +} diff --git a/GoonMod/mods/custom_colours/weapon_flashlight.lua b/GoonMod/mods/custom_colours/weapon_flashlight.lua new file mode 100644 index 0000000..85c17f4 --- /dev/null +++ b/GoonMod/mods/custom_colours/weapon_flashlight.lua @@ -0,0 +1,213 @@ + +-- Mod Definition +local Mod = class( BaseMod ) +Mod.id = "CustomWeaponFlashlight" +Mod.Name = "Custom Weapon Flashlight Colour" +Mod.Desc = "Set a custom colour for flashlights attached to your weapons" +Mod.Requirements = {} +Mod.Incompatibilities = {} +Mod.Priority = 1 + +Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() + GoonBase.Mods:RegisterMod( Mod ) +end) + +if not Mod:IsEnabled() then + return +end + +-- Weapon Light +GoonBase.WeaponLight = GoonBase.WeaponLight or {} +local Light = GoonBase.WeaponLight +Light.MenuFile = "custom_flashlight_menu.txt" + +-- Options +GoonBase.Options.WeaponLight = GoonBase.Options.WeaponLight or {} +GoonBase.Options.WeaponLight.Enabled = GoonBase.Options.WeaponLight.Enabled or true +GoonBase.Options.WeaponLight.RH = GoonBase.Options.WeaponLight.RH or 1 +GoonBase.Options.WeaponLight.GS = GoonBase.Options.WeaponLight.GS or 1 +GoonBase.Options.WeaponLight.BV = GoonBase.Options.WeaponLight.BV or 1 +GoonBase.Options.WeaponLight.UseHSV = GoonBase.Options.WeaponLight.UseHSV or false +GoonBase.Options.WeaponLight.UseRainbow = GoonBase.Options.WeaponLight.UseRainbow or false +GoonBase.Options.WeaponLight.RainbowSpeed = GoonBase.Options.WeaponLight.RainbowSpeed or 1 + +-- Light Colour +Light.Color = ColorHSVRGB:new( GoonBase.Options.WeaponLight, Color.white ) + +-- Functions +function Light:IsEnabled() + return GoonBase.Options.WeaponLight.Enabled +end + +function Light:IsRainbow() + return GoonBase.Options.WeaponLight.UseRainbow +end + +function Light:RainbowSpeed() + return GoonBase.Options.WeaponLight.RainbowSpeed +end + +function Light:GetColor(alpha) + return Light.Color:GetColor( alpha ) +end + +function Light:ShowPreviewMenuItem() + + if not managers.menu_component then + return + end + + local ws = managers.menu_component._ws + self._panel = ws:panel():panel() + + local w, h = self._panel:w() * 0.35, 48 + self._color_rect = self._panel:rect({ + w = w, + h = h, + color = Color.red, + blend_mode = "add", + layer = tweak_data.gui.MOUSE_LAYER - 50, + }) + self._color_rect:set_right( self._panel:right() ) + self._color_rect:set_top( self._panel:h() * 0.265 ) + + self:UpdatePreview() + +end + +function Light:DestroyPreviewMenuItem() + + if alive(self._panel) then + + self._panel:remove( self._color_rect ) + self._panel:remove( self._panel ) + + self._color_rect = nil + self._panel = nil + + end + +end + +function Light:UpdatePreview( t ) + + if not alive(self._panel) or not alive(self._color_rect) or not Light.Color then + return + end + + if self:IsRainbow() and t then + self._color_rect:set_color( Light.Color:GetRainbowColor(t, self:RainbowSpeed()) ) + else + self._color_rect:set_color( Light.Color:GetColor() ) + end + +end + +-- Menu +Hooks:Add("MenuManagerInitialize", "MenuManagerInitialize_" .. Mod:ID(), function( menu_manager ) + + -- Callbacks + MenuCallbackHandler.CustomFlashlightMenuChangeFocus = function( node, focus ) + if focus then + Light:ShowPreviewMenuItem() + else + Light:DestroyPreviewMenuItem() + end + end + + MenuCallbackHandler.ToggleEnableCustomFlashlight = function( this, item ) + GoonBase.Options.WeaponLight.Enabled = item:value() == "on" and true or false + Light:UpdatePreview() + end + + MenuCallbackHandler.CustomFlashlightToggleUseHue = function( this, item ) + GoonBase.Options.WeaponLight.UseHSV = item:value() == "on" and true or false + Light:UpdatePreview() + end + + MenuCallbackHandler.CustomFlashlightSetRedHue = function( this, item ) + GoonBase.Options.WeaponLight.RH = tonumber( item:value() ) + Light:UpdatePreview() + end + + MenuCallbackHandler.CustomFlashlightSetGreenSaturation = function( this, item ) + GoonBase.Options.WeaponLight.GS = tonumber( item:value() ) + Light:UpdatePreview() + end + + MenuCallbackHandler.CustomFlashlightSetBlueValue = function( this, item ) + GoonBase.Options.WeaponLight.BV = tonumber( item:value() ) + Light:UpdatePreview() + end + + MenuCallbackHandler.CustomFlashlightSetUseRainbow = function( this, item ) + GoonBase.Options.WeaponLight.UseRainbow = item:value() == "on" and true or false + Light:UpdatePreview() + end + + MenuCallbackHandler.CustomFlashlightSetRainbowSpeed = function( this, item ) + GoonBase.Options.WeaponLight.RainbowSpeed = tonumber( item:value() ) + end + + MenuHelper:LoadFromJsonFile( GoonBase.MenusPath .. Light.MenuFile, GoonBase.WeaponLight, GoonBase.Options.WeaponLight ) + +end) + +-- Hooks +Hooks:Add("MenuUpdate", "MenuUpdate_" .. Mod:ID(), function(t, dt) + if Light:IsRainbow() then + Light:UpdatePreview( t ) + end +end) + +Hooks:Add("GameSetupUpdate", "GameSetupUpdate_" .. Mod:ID(), function(t, dt) + if Light:IsRainbow() then + Light:UpdatePreview( t ) + end +end) + +Hooks:Add("WeaponFlashLightInit", "WeaponFlashLightInit_CustomLight", function(flashlight, unit) + + if not Light:IsEnabled() then + Hooks:Remove("WeaponFlashLightInit_CustomLight") + return + end + + flashlight._light:set_color( Light:GetColor() ) + +end) + +Hooks:Add("WeaponFlashLightCheckState", "WeaponFlashLightCheckState_CustomLight", function(flashlight) + + if flashlight._on then + + flashlight._unit:set_extension_update_enabled( Idstring("base"), flashlight._on ) + + Hooks:RegisterHook("WeaponFlashLightUpdate") + flashlight._old_update = flashlight.update + flashlight.update = function(self, unit, t, dt) + Hooks:Call( "WeaponFlashLightUpdate", self, unit, t, dt ) + if flashlight.overkill_update ~= nil then + flashlight.overkill_update(self, unit, t, dt) + end + end + + end + +end) + +Hooks:Add("WeaponFlashLightUpdate", "WeaponFlashLightUpdate_Rainbow", function(flashlight, unit, t, dt) + + if not Light:IsEnabled() then + return + end + + if not Light:IsRainbow() then + flashlight._light:set_color( Light:GetColor() ) + end + + if Light:IsRainbow() then + flashlight._light:set_color( Light.Color:GetRainbowColor( t, Light:RainbowSpeed() ) ) + end + +end) diff --git a/GoonMod/mods/custom_colours/world_laser_colors.lua b/GoonMod/mods/custom_colours/world_laser_colors.lua new file mode 100644 index 0000000..c6dd51c --- /dev/null +++ b/GoonMod/mods/custom_colours/world_laser_colors.lua @@ -0,0 +1,211 @@ + +-- Mod Definition +local Mod = class( BaseMod ) +Mod.id = "CustomWorldLaserColour" +Mod.Name = "Custom World Laser Colour" +Mod.Desc = "Set a custom colour for lasers that appear in the game world" +Mod.Requirements = {} +Mod.Incompatibilities = {} +Mod.Priority = 1 + +Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() + GoonBase.Mods:RegisterMod( Mod ) +end) + +if not Mod:IsEnabled() then + return +end + +-- Lasers +GoonBase.WorldLasers = GoonBase.WorldLasers or {} +local Lasers = GoonBase.WorldLasers +Lasers.MenuFile = "custom_world_laser_menu.txt" +Lasers._WorldOpacity = 0.4 + +-- Options +GoonBase.Options.WorldLasers = GoonBase.Options.WorldLasers or {} +GoonBase.Options.WorldLasers.Enabled = GoonBase.Options.WorldLasers.Enabled or true +GoonBase.Options.WorldLasers.RH = GoonBase.Options.WorldLasers.RH or 0.6 +GoonBase.Options.WorldLasers.GS = GoonBase.Options.WorldLasers.GS or 0.0 +GoonBase.Options.WorldLasers.BV = GoonBase.Options.WorldLasers.BV or 0.0 +GoonBase.Options.WorldLasers.UseHSV = GoonBase.Options.WorldLasers.UseHSV or false +GoonBase.Options.WorldLasers.UseRainbow = GoonBase.Options.WorldLasers.UseRainbow or false +GoonBase.Options.WorldLasers.RainbowSpeed = GoonBase.Options.WorldLasers.RainbowSpeed or 1 + +-- Laser Colour +Lasers.Color = ColorHSVRGB:new( GoonBase.Options.WorldLasers, Color.red:with_alpha(0.6) ) + +-- Functions +function Lasers:IsEnabled() + return GoonBase.Options.WorldLasers.Enabled +end + +function Lasers:IsRainbow() + return GoonBase.Options.UseRainbow.Rainbow +end + +function Lasers:GetColor(alpha) + if Lasers.Color == nil then + Lasers.Color = ColorHSVRGB:new() + Lasers.Color:SetOptionsTable( "WorldLasers" ) + end + return Lasers.Color:GetColor( alpha ) or Color( alpha or 1, 1, 0, 0 ) +end + + +function Lasers:IsEnabled() + return GoonBase.Options.WorldLasers.Enabled +end + +function Lasers:IsRainbow() + return GoonBase.Options.WorldLasers.UseRainbow +end + +function Lasers:RainbowSpeed() + return GoonBase.Options.WorldLasers.RainbowSpeed +end + +function Lasers:GetColor(alpha) + return Lasers.Color:GetColor( alpha ) +end + +function Lasers:ShowPreviewMenuItem() + + if not managers.menu_component then + return + end + + local ws = managers.menu_component._ws + self._panel = ws:panel():panel() + + local w, h = self._panel:w() * 0.35, 48 + self._color_rect = self._panel:rect({ + w = w, + h = h, + color = Color.red, + blend_mode = "add", + layer = tweak_data.gui.MOUSE_LAYER - 50, + }) + self._color_rect:set_right( self._panel:right() ) + self._color_rect:set_top( self._panel:h() * 0.265 ) + + self:UpdatePreview() + +end + +function Lasers:DestroyPreviewMenuItem() + + if alive(self._panel) then + + self._panel:remove( self._color_rect ) + self._panel:remove( self._panel ) + + self._color_rect = nil + self._panel = nil + + end + +end + +function Lasers:UpdatePreview( t ) + + if not alive(self._panel) or not alive(self._color_rect) or not Lasers.Color then + return + end + + if self:IsRainbow() and t then + self._color_rect:set_color( Lasers.Color:GetRainbowColor(t, self:RainbowSpeed()) ) + else + self._color_rect:set_color( Lasers.Color:GetColor() ) + end + +end + +-- Menu +Hooks:Add("MenuManagerInitialize", "MenuManagerInitialize_" .. Mod:ID(), function( menu_manager ) + + -- Callbacks + MenuCallbackHandler.CustomWorldLaserMenuChangeFocus = function( node, focus ) + if focus then + Lasers:ShowPreviewMenuItem() + else + Lasers:DestroyPreviewMenuItem() + end + end + + MenuCallbackHandler.ToggleEnableCustomWorldLaser = function( this, item ) + GoonBase.Options.WorldLasers.Enabled = item:value() == "on" and true or false + Lasers:UpdatePreview() + end + + MenuCallbackHandler.CustomWorldLaserToggleUseHue = function( this, item ) + GoonBase.Options.WorldLasers.UseHSV = item:value() == "on" and true or false + Lasers:UpdatePreview() + end + + MenuCallbackHandler.CustomWorldLaserSetRedHue = function( this, item ) + GoonBase.Options.WorldLasers.RH = tonumber( item:value() ) + Lasers:UpdatePreview() + end + + MenuCallbackHandler.CustomWorldLaserSetGreenSaturation = function( this, item ) + GoonBase.Options.WorldLasers.GS = tonumber( item:value() ) + Lasers:UpdatePreview() + end + + MenuCallbackHandler.CustomWorldLaserSetBlueValue = function( this, item ) + GoonBase.Options.WorldLasers.BV = tonumber( item:value() ) + Lasers:UpdatePreview() + end + + MenuCallbackHandler.CustomWorldLaserSetUseRainbow = function( this, item ) + GoonBase.Options.WorldLasers.UseRainbow = item:value() == "on" and true or false + Lasers:UpdatePreview() + end + + MenuCallbackHandler.CustomWorldLaserSetRainbowSpeed = function( this, item ) + GoonBase.Options.WorldLasers.RainbowSpeed = tonumber( item:value() ) + end + + MenuHelper:LoadFromJsonFile( GoonBase.MenusPath .. Lasers.MenuFile, GoonBase.WorldLasers, GoonBase.Options.WorldLasers ) + +end) + +-- Hooks +Hooks:Add("MenuUpdate", "MenuUpdate_" .. Mod:ID(), function(t, dt) + if Lasers:IsRainbow() then + Lasers:UpdatePreview( t ) + end +end) + +Hooks:Add("GameSetupUpdate", "GameSetupUpdate_" .. Mod:ID(), function(t, dt) + if Lasers:IsRainbow() then + Lasers:UpdatePreview( t ) + end +end) + +Hooks:Add("ElementLaserTriggerPostInit", "ElementLaserTriggerPostInit_WorldLaser", function(laser) + + if not Lasers:IsEnabled() then + return + end + + laser._brush:set_color( Lasers:GetColor(Lasers._WorldOpacity) ) + +end) + +Hooks:Add("ElementLaserTriggerUpdateDraw", "ElementLaserTriggerUpdateDraw_WorldLaser", function(laser, t, dt) + + if not Lasers:IsEnabled() then + return + end + + if not Lasers:IsRainbow() then + laser._brush:set_color( Lasers:GetColor(Lasers._WorldOpacity) ) + end + + if Lasers:IsRainbow() then + laser._brush:set_color( Lasers.Color:GetRainbowColor( t, Lasers:RainbowSpeed() ):with_alpha(Lasers._WorldOpacity) ) + end + +end) diff --git a/GoonMod/mods/disabled/colors/color_hsvrgb.lua b/GoonMod/mods/disabled/colors/color_hsvrgb.lua deleted file mode 100644 index da7c004..0000000 --- a/GoonMod/mods/disabled/colors/color_hsvrgb.lua +++ /dev/null @@ -1,216 +0,0 @@ - -ColorHSVRGB = class() - -function ColorHSVRGB:init() - self._ID = "DefaultID" - self._menu_id = nil - self._using_hsv = false - self._priority = 0 - self._r = 0 - self._g = 0 - self._b = 0 -end - -function ColorHSVRGB:ID() - return self._ID -end - -function ColorHSVRGB:UsingHSV() - return self._using_hsv -end - -function ColorHSVRGB:GetColor(alpha) - - local r, g, b = 0, 0, 0 - if self:UsingHSV() then - r, g, b = self:ToRGB(self._r, self._g, self._b) - else - r = self._r - g = self._g - b = self._b - end - - return Color(alpha or 1, r, g, b) - -end - -function ColorHSVRGB:ToRGB(h, s, v) - - local r, g, b - - local i = math.floor(h * 6) - local f = h * 6 - i - local p = v * (1 - s) - local q = v * (1 - f * s) - local t = v * (1 - (1 - f) * s) - - local mod = i % 6 - if mod == 0 then - r = v - g = t - b = p - elseif mod == 1 then - r = q - g = v - b = p - elseif mod == 2 then - r = p - g = v - b = t - elseif mod == 3 then - r = p - g = q - b = v - elseif mod == 4 then - r = t - g = p - b = v - elseif mod == 5 then - r = v - g = p - b = q - end - - return r, g, b - -end - -function ColorHSVRGB:SetID(id) - self._ID = id -end - -function ColorHSVRGB:SetHSV(hsv) - self._using_hsv = hsv -end - -function ColorHSVRGB:SetOptionsTable(tbl) - - self.options_table = tbl - - self._r = GoonBase.Options[tbl].R - self._g = GoonBase.Options[tbl].G - self._b = GoonBase.Options[tbl].B - self._using_hsv = GoonBase.Options[tbl].HSV - -end - -function ColorHSVRGB:SetupLocalization() - - local id = self:ID() - local hsv = self:UsingHSV() - local loc = managers.localization._custom_localizations - - loc["Options_ToggleColorHSV_Title"] = "Use HSV" - loc["Options_ToggleColorHSV_Message"] = "Use HSV instead of RGB" - - loc["Options_" .. id .. "_RH_Title"] = hsv and "Hue/Red" or "Red/Hue" - loc["Options_" .. id .. "_GS_Title"] = hsv and "Saturation/Green" or "Green/Saturation" - loc["Options_" .. id .. "_BV_Title"] = hsv and "Value/Blue" or "Blue/Value" - loc["Options_" .. id .. "_Example"] = "Color Example" - - loc["Options_" .. id .. "_RH_Desc"] = string.gsub( "Control the {1} of the colour", "{1}", hsv and "Hue" or "Red" ) - loc["Options_" .. id .. "_GS_Desc"] = string.gsub( "Control the {1} of the colour", "{1}", hsv and "Saturation" or "Green" ) - loc["Options_" .. id .. "_BV_Desc"] = string.gsub( "Control the {1} of the colour", "{1}", hsv and "Value" or "Blue" ) - loc["Options_" .. id .. "_ExampleMessage"] = "Re-open this menu to update the color example" - - managers.localization._custom_localizations = loc - -end - -function ColorHSVRGB:SetPriority( prio ) - self._priority = prio -end - -function ColorHSVRGB:SetupMenu( menu_id ) - - local id = self:ID() - local r = GoonBase.Options[self.options_table].R - local g = GoonBase.Options[self.options_table].G - local b = GoonBase.Options[self.options_table].B - local hsv = GoonBase.Options[self.options_table].HSV - - self:SetupLocalization() - self._menu_id = menu_id - - MenuCallbackHandler["set_rh_" .. id] = function(this, item) - self._r = item:value() - GoonBase.Options[self.options_table].R = self._r - GoonBase.Options:Save() - end - - MenuCallbackHandler["set_gs_" .. id] = function(this, item) - self._g = item:value() - GoonBase.Options[self.options_table].G = self._g - GoonBase.Options:Save() - end - - MenuCallbackHandler["set_bv_" .. id] = function(this, item) - self._b = item:value() - GoonBase.Options[self.options_table].B = self._b - GoonBase.Options:Save() - end - - MenuCallbackHandler["toggle_hsv_" .. id] = function(this, item) - - local enabled = item:value() == "on" and true or false - self:SetHSV( enabled ) - GoonBase.Options[self.options_table].HSV = enabled - GoonBase.Options:Save() - - self:SetupLocalization() - - end - - MenuHelper:AddSlider({ - id = "slider_rh_" .. id, - title = "Options_" .. id .. "_RH_Title", - desc = "Options_" .. id .. "_RH_Desc", - callback = "set_rh_" .. id, - value = r, - min = 0, - max = 1, - step = 0.01, - show_value = true, - menu_id = menu_id, - priority = self._priority + 5, - }) - - MenuHelper:AddSlider({ - id = "slider_gs_" .. id, - title = "Options_" .. id .. "_GS_Title", - desc = "Options_" .. id .. "_GS_Desc", - callback = "set_gs_" .. id, - value = g, - min = 0, - max = 1, - step = 0.01, - show_value = true, - menu_id = menu_id, - priority = self._priority + 4, - }) - - MenuHelper:AddSlider({ - id = "slider_bv_" .. id, - title = "Options_" .. id .. "_BV_Title", - desc = "Options_" .. id .. "_BV_Desc", - callback = "set_bv_" .. id, - value = b, - min = 0, - max = 1, - step = 0.01, - show_value = true, - menu_id = menu_id, - priority = self._priority + 3, - }) - - MenuHelper:AddToggle({ - id = "toggle_hsv_" .. id, - title = "Options_ToggleColorHSV_Title", - desc = "Options_ToggleColorHSV_Message", - callback = "toggle_hsv_" .. id, - value = hsv, - menu_id = menu_id, - priority = self._priority, - }) - -end diff --git a/GoonMod/mods/disabled/colors/weapon_flashlight.lua b/GoonMod/mods/disabled/colors/weapon_flashlight.lua deleted file mode 100644 index df3f743..0000000 --- a/GoonMod/mods/disabled/colors/weapon_flashlight.lua +++ /dev/null @@ -1,194 +0,0 @@ - --- Mod Definition -local Mod = class( BaseMod ) -Mod.id = "CustomWeaponFlashlight" -Mod.Name = "Custom Weapon Flashlight Colour" -Mod.Desc = "Set a custom colour for flashlights attached to your weapons" -Mod.Requirements = {} -Mod.Incompatibilities = {} -Mod.Priority = 1 - -Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() - GoonBase.Mods:RegisterMod( Mod ) -end) - -if not Mod:IsEnabled() then - return -end - --- Weapon Light -GoonBase.WeaponLight = GoonBase.WeaponLight or {} -local Light = GoonBase.WeaponLight -Light.MenuId = "goonbase_weapon_light_menu" -Light.Color = nil - --- Options -if GoonBase.Options.WeaponLight == nil then - GoonBase.Options.WeaponLight = {} - GoonBase.Options.WeaponLight.Enabled = false - GoonBase.Options.WeaponLight.R = 1 - GoonBase.Options.WeaponLight.G = 0.1 - GoonBase.Options.WeaponLight.B = 0.1 - GoonBase.Options.WeaponLight.HSV = false - GoonBase.Options.WeaponLight.Rainbow = false - GoonBase.Options.WeaponLight.RainbowSpeed = 10 -end - --- Functions -function Light:IsEnabled() - return GoonBase.Options.WeaponLight.Enabled -end - -function Light:IsRainbow() - return GoonBase.Options.WeaponLight.Rainbow -end - -function Light:GetColor(alpha) - if Light.Color == nil then - Light.Color = ColorHSVRGB:new() - Light.Color:SetOptionsTable( "WeaponLight" ) - end - return Light.Color:GetColor( alpha ) or Color( alpha or 1, 1, 0, 0 ) -end - --- Menu -Hooks:Add("MenuManagerSetupCustomMenus", "MenuManagerSetupCustomMenus_WeaponLight", function(menu_manager, menu_nodes) - MenuHelper:NewMenu( Light.MenuId ) -end) - -Hooks:Add("MenuManagerSetupGoonBaseMenu", "MenuManagerSetupGoonBaseMenu_WeaponLight", function(menu_manager, menu_nodes) - - -- Submenu Button - MenuHelper:AddButton({ - id = "weapon_light_button", - title = "Options_WeaponLightName", - desc = "Options_WeaponLightDesc", - next_node = Light.MenuId, - menu_id = "goonbase_options_menu", - }) - - -- Enabled Toggle - MenuCallbackHandler.toggle_custom_weapon_light_color = function(this, item) - GoonBase.Options.WeaponLight.Enabled = item:value() == "on" and true or false - GoonBase.Options:Save() - end - - MenuHelper:AddToggle({ - id = "toggle_custom_weapon_light_color", - title = "Options_WeaponLightEnableTitle", - desc = "Options_WeaponLightEnableDesc", - callback = "toggle_custom_weapon_light_color", - value = GoonBase.Options.WeaponLight.Enabled, - menu_id = Light.MenuId, - priority = 11 - }) - - -- RGB/HSV Colour - Light.Color = ColorHSVRGB:new() - Light.Color:SetID( "weapon_light" ) - Light.Color:SetPriority( 5 ) - Light.Color:SetOptionsTable( "WeaponLight" ) - Light.Color:SetupMenu( Light.MenuId ) - - -- Rainbow Laser - MenuCallbackHandler.toggle_weapon_light_rainbow = function(this, item) - GoonBase.Options.WeaponLight.Rainbow = item:value() == "on" and true or false - GoonBase.Options:Save() - end - - MenuCallbackHandler.weapon_light_rainbow_speed = function(this, item) - GoonBase.Options.WeaponLight.RainbowSpeed = item:value() - GoonBase.Options:Save() - end - - MenuHelper:AddToggle({ - id = "toggle_weapon_light_rainbow", - title = "Options_WeaponLightRainbowTitle", - desc = "Options_WeaponLightRainbowDesc", - callback = "toggle_weapon_light_rainbow", - value = GoonBase.Options.WeaponLight.Rainbow, - menu_id = Light.MenuId, - priority = 2 - }) - - MenuHelper:AddSlider({ - id = "weapon_light_rainbow_speed", - title = "Options_WeaponLightRainbowSpeedTitle", - desc = "Options_WeaponLightRainbowSpeedDesc", - callback = "weapon_light_rainbow_speed", - value = GoonBase.Options.WeaponLight.RainbowSpeed, - min = 1, - max = 100, - step = 1, - show_value = true, - menu_id = Light.MenuId, - priority = 1, - }) - -end) - -Hooks:Add("MenuManagerBuildCustomMenus", "MenuManagerBuildCustomMenus_WeaponLight", function(menu_manager, mainmenu_nodes) - local menu_id = Light.MenuId - local data = { - area_bg = "half" - } - mainmenu_nodes[menu_id] = MenuHelper:BuildMenu( menu_id, data ) -end) - --- Hooks -Hooks:Add("WeaponFlashLightInit", "WeaponFlashLightInit_CustomLight", function(flashlight, unit) - - if not Light:IsEnabled() then - Hooks:Remove("WeaponFlashLightInit_CustomLight") - return - end - - flashlight._light:set_color( Light:GetColor() ) - -end) - -Hooks:Add("WeaponFlashLightCheckState", "WeaponFlashLightCheckState_CustomLight", function(flashlight) - - if flashlight._on then - - flashlight._unit:set_extension_update_enabled( Idstring("base"), flashlight._on ) - - Hooks:RegisterHook("WeaponFlashLightUpdate") - flashlight._old_update = flashlight.update - flashlight.update = function(self, unit, t, dt) - Hooks:Call( "WeaponFlashLightUpdate", self, unit, t, dt ) - if flashlight.overkill_update ~= nil then - flashlight.overkill_update(self, unit, t, dt) - end - end - - end - -end) - -Hooks:Add("WeaponFlashLightUpdate", "WeaponFlashLightUpdate_Rainbow", function(flashlight, unit, t, dt) - - local psuccess, perror = pcall(function() - - if not Light:IsEnabled() then - return - end - - if not Light:IsRainbow() then - flashlight._light:set_color( Light:GetColor() ) - end - - if Light:IsRainbow() then - - Light:GetColor() - local r, g, b = Light.Color:ToRGB( math.sin(GoonBase.Options.WeaponLight.RainbowSpeed * t), GoonBase.Options.WeaponLight.G, GoonBase.Options.WeaponLight.B ) - flashlight._light:set_color( Color(r * 2, g * 2, b * 2) ) - - end - - end) - if not psuccess then - Print("[Error] " .. perror) - end - -end) diff --git a/GoonMod/mods/disabled/colors/world_laser_colors.lua b/GoonMod/mods/disabled/colors/world_laser_colors.lua deleted file mode 100644 index 324a104..0000000 --- a/GoonMod/mods/disabled/colors/world_laser_colors.lua +++ /dev/null @@ -1,165 +0,0 @@ - --- Mod Definition -local Mod = class( BaseMod ) -Mod.id = "CustomWorldLaserColour" -Mod.Name = "Custom World Laser Colour" -Mod.Desc = "Set a custom colour for lasers that appear in the game world" -Mod.Requirements = {} -Mod.Incompatibilities = {} -Mod.Priority = 1 - -Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() - GoonBase.Mods:RegisterMod( Mod ) -end) - -if not Mod:IsEnabled() then - return -end - --- Lasers -GoonBase.WorldLasers = GoonBase.WorldLasers or {} -local Lasers = GoonBase.WorldLasers -Lasers.MenuId = "goonbase_world_laser_menu" -Lasers.Color = nil - --- Options -if GoonBase.Options.WorldLasers == nil then - GoonBase.Options.WorldLasers = {} - GoonBase.Options.WorldLasers.Enabled = false - GoonBase.Options.WorldLasers.R = 1 - GoonBase.Options.WorldLasers.G = 0.1 - GoonBase.Options.WorldLasers.B = 0.1 - GoonBase.Options.WorldLasers.HSV = false - GoonBase.Options.WorldLasers.Rainbow = false - GoonBase.Options.WorldLasers.RainbowSpeed = 10 -end - --- Functions -function Lasers:IsEnabled() - return GoonBase.Options.WorldLasers.Enabled -end - -function Lasers:IsRainbow() - return GoonBase.Options.WorldLasers.Rainbow -end - -function Lasers:GetColor(alpha) - if Lasers.Color == nil then - Lasers.Color = ColorHSVRGB:new() - Lasers.Color:SetOptionsTable( "WorldLasers" ) - end - return Lasers.Color:GetColor( alpha ) or Color( alpha or 1, 1, 0, 0 ) -end - --- Menu -Hooks:Add("MenuManagerSetupCustomMenus", "MenuManagerSetupCustomMenus_WorldLaser", function(menu_manager, menu_nodes) - MenuHelper:NewMenu( Lasers.MenuId ) -end) - -Hooks:Add("MenuManagerSetupGoonBaseMenu", "MenuManagerSetupGoonBaseMenu_WorldLaser", function(menu_manager, menu_nodes) - - -- Submenu Button - MenuHelper:AddButton({ - id = "world_laser_button", - title = "Options_WorldLaserName", - desc = "Options_WorldLaserDesc", - next_node = Lasers.MenuId, - menu_id = "goonbase_options_menu", - }) - - -- Enabled Toggle - MenuCallbackHandler.toggle_custom_world_laser_color = function(this, item) - GoonBase.Options.WorldLasers.Enabled = item:value() == "on" and true or false - GoonBase.Options:Save() - end - - MenuHelper:AddToggle({ - id = "toggle_custom_world_laser_color", - title = "Options_WorldLaserEnableTitle", - desc = "Options_WorldLaserEnableDesc", - callback = "toggle_custom_world_laser_color", - value = GoonBase.Options.WorldLasers.Enabled, - menu_id = Lasers.MenuId, - priority = 11 - }) - - -- RGB/HSV Colour - Lasers.Color = ColorHSVRGB:new() - Lasers.Color:SetID( "world_laser" ) - Lasers.Color:SetPriority( 5 ) - Lasers.Color:SetOptionsTable( "WorldLasers" ) - Lasers.Color:SetupMenu( Lasers.MenuId ) - - -- Rainbow Laser - MenuCallbackHandler.toggle_world_laser_rainbow = function(this, item) - GoonBase.Options.WorldLasers.Rainbow = item:value() == "on" and true or false - GoonBase.Options:Save() - end - - MenuCallbackHandler.world_laser_rainbow_speed = function(this, item) - GoonBase.Options.WorldLasers.RainbowSpeed = item:value() - GoonBase.Options:Save() - end - - MenuHelper:AddToggle({ - id = "toggle_world_laser_rainbow", - title = "Options_WorldLaserRainbowTitle", - desc = "Options_WorldLaserRainbowDesc", - callback = "toggle_world_laser_rainbow", - value = GoonBase.Options.WorldLasers.Rainbow, - menu_id = Lasers.MenuId, - priority = 2 - }) - - MenuHelper:AddSlider({ - id = "world_laser_rainbow_speed", - title = "Options_WorldLaserRainbowSpeedTitle", - desc = "Options_WorldLaserRainbowSpeedDesc", - callback = "world_laser_rainbow_speed", - value = GoonBase.Options.WorldLasers.RainbowSpeed, - min = 1, - max = 100, - step = 1, - show_value = true, - menu_id = Lasers.MenuId, - priority = 1, - }) - -end) - -Hooks:Add("MenuManagerBuildCustomMenus", "MenuManagerBuildCustomMenus_WorldLaser", function(menu_manager, mainmenu_nodes) - local menu_id = Lasers.MenuId - local data = { - area_bg = "half" - } - mainmenu_nodes[menu_id] = MenuHelper:BuildMenu( menu_id, data ) -end) - --- Hooks -Hooks:Add("ElementLaserTriggerPostInit", "ElementLaserTriggerPostInit_WorldLaser", function(laser) - - if not Lasers:IsEnabled() then - return - end - - laser._brush:set_color( Lasers:GetColor(0.15) ) - -end) - -Hooks:Add("ElementLaserTriggerUpdateDraw", "ElementLaserTriggerUpdateDraw_WorldLaser", function(laser, t, dt) - - if not Lasers:IsEnabled() then - return - end - - if not Lasers:IsRainbow() then - laser._brush:set_color( Lasers:GetColor() ) - end - - if Lasers:IsRainbow() then - Lasers:GetColor() - local r, g, b = Lasers.Color:ToRGB( math.sin(GoonBase.Options.WorldLasers.RainbowSpeed * t), GoonBase.Options.WorldLasers.G, GoonBase.Options.WorldLasers.B ) - laser._brush:set_color( Color(0.2, r, g, b) ) - end - -end) From 877a1a03fe6a45dc2b4d2ae3811c43037e89ad2a Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Thu, 19 Mar 2015 16:20:39 +1100 Subject: [PATCH 68/92] Gage Coins wallet display on end heist screen only appears when cash display appears --- GoonMod/mods/gage_coins.lua | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/GoonMod/mods/gage_coins.lua b/GoonMod/mods/gage_coins.lua index ad7b454..d567f51 100644 --- a/GoonMod/mods/gage_coins.lua +++ b/GoonMod/mods/gage_coins.lua @@ -188,3 +188,18 @@ Hooks:Add("WalletGuiObjectOnRefresh", "WalletGuiObjectOnRefresh_" .. Mod:ID(), f end end) + +Hooks:Add("WalletGuiObjectSetObjectVisible", "WalletGuiObjectSetObjectVisible_" .. Mod:ID(), function() + + local gage_coins_icon = Global.wallet_panel:child("gage_coins_icon") + local gage_coins_text = Global.wallet_panel:child("gage_coins_text") + local money_icon = Global.wallet_panel:child("wallet_money_icon") + + if gage_coins_icon then + gage_coins_icon:set_visible( money_icon:visible() ) + end + if gage_coins_text then + gage_coins_text:set_visible( money_icon:visible() ) + end + +end) From 8152705f0a8845b00c9bb8098c4e7a3d66d02350 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Thu, 19 Mar 2015 16:23:11 +1100 Subject: [PATCH 69/92] Safety check wallet GUI --- GoonMod/mods/gage_coins.lua | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/GoonMod/mods/gage_coins.lua b/GoonMod/mods/gage_coins.lua index d567f51..6bf305e 100644 --- a/GoonMod/mods/gage_coins.lua +++ b/GoonMod/mods/gage_coins.lua @@ -191,15 +191,19 @@ end) Hooks:Add("WalletGuiObjectSetObjectVisible", "WalletGuiObjectSetObjectVisible_" .. Mod:ID(), function() - local gage_coins_icon = Global.wallet_panel:child("gage_coins_icon") - local gage_coins_text = Global.wallet_panel:child("gage_coins_text") - local money_icon = Global.wallet_panel:child("wallet_money_icon") + if Global.wallet_panel then + + local gage_coins_icon = Global.wallet_panel:child("gage_coins_icon") + local gage_coins_text = Global.wallet_panel:child("gage_coins_text") + local money_icon = Global.wallet_panel:child("wallet_money_icon") + + if gage_coins_icon then + gage_coins_icon:set_visible( money_icon:visible() ) + end + if gage_coins_text then + gage_coins_text:set_visible( money_icon:visible() ) + end - if gage_coins_icon then - gage_coins_icon:set_visible( money_icon:visible() ) - end - if gage_coins_text then - gage_coins_text:set_visible( money_icon:visible() ) end end) From 75f48895ba4e932c994dfa8397ff0c9a7ffccebc Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Fri, 20 Mar 2015 21:34:37 +1100 Subject: [PATCH 70/92] Fixed weapon customizer "Revert to Factory Standard" button not working --- GoonMod/goonbase.lua | 1 + GoonMod/mods/weapon_customization_menus.lua | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/GoonMod/goonbase.lua b/GoonMod/goonbase.lua index 14f168d..a4c1706 100644 --- a/GoonMod/goonbase.lua +++ b/GoonMod/goonbase.lua @@ -82,6 +82,7 @@ GoonBase.HookFiles = { -- ["lib/managers/challengemanager"] = "ChallengeManager.lua", ["lib/managers/menu/playerprofileguiobject"] = "PlayerProfileGUIObject.lua", ["lib/managers/menu/walletguiobject"] = "WalletGUIObject.lua", + ["lib/managers/experiencemanager"] = "ExperienceManager.lua", } diff --git a/GoonMod/mods/weapon_customization_menus.lua b/GoonMod/mods/weapon_customization_menus.lua index bb0a2ca..baddfec 100644 --- a/GoonMod/mods/weapon_customization_menus.lua +++ b/GoonMod/mods/weapon_customization_menus.lua @@ -766,7 +766,7 @@ end function WeaponCustomization:_AdvancedClearWeaponAccept() - if managers.blackmarket._customizing_weapon_data then + if not managers.blackmarket._customizing_weapon_data then return end From d6ae90d3f66e390e06726f91b4639bc891a31aef Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Fri, 20 Mar 2015 21:34:57 +1100 Subject: [PATCH 71/92] Added mod to restore Infamy levels displaying as roman numerals --- GoonMod/mods/roman_infamy.lua | 60 +++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 GoonMod/mods/roman_infamy.lua diff --git a/GoonMod/mods/roman_infamy.lua b/GoonMod/mods/roman_infamy.lua new file mode 100644 index 0000000..6e45e32 --- /dev/null +++ b/GoonMod/mods/roman_infamy.lua @@ -0,0 +1,60 @@ + +-- Mod Definition +local Mod = class( BaseMod ) +Mod.id = "RomanInfamy" +Mod.Name = "Infamy Roman Numerals" +Mod.Desc = "Restores the infamy system to use Roman instead of Arabic numerals" +Mod.Requirements = {} +Mod.Incompatibilities = {} + +Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() + GoonBase.Mods:RegisterMod( Mod ) +end) + +if not Mod:IsEnabled() then + return +end + +Hooks:Add("ExperienceManagerGetRankString", "ExperienceManagerGetRankString_" .. Mod:ID(), function(exp_manager, rank) + + -- Credit to Overkill Software for the original code + if not rank or rank <= 0 then + return nil + end + local numbers = { + 1, + 5, + 10, + 50, + 100, + 500, + 1000 + } + local chars = { + "I", + "V", + "X", + "L", + "C", + "D", + "M" + } + local roman = "" + for i = #numbers, 1, -1 do + local num = numbers[i] + while rank - num >= 0 and rank > 0 do + roman = roman .. chars[i] + rank = rank - num + end + for j = 1, i - 1 do + local num2 = numbers[j] + if rank - (num - num2) >= 0 and num > rank and rank > 0 and num - num2 ~= num2 then + roman = roman .. chars[j] .. chars[i] + rank = rank - (num - num2) + break + end + end + end + return roman + +end) From bb21e33dd66ef4cbf225c489000214e6b1775125 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Fri, 20 Mar 2015 21:36:51 +1100 Subject: [PATCH 72/92] Renamed custom laser/light colours --- GoonMod/mod.txt | 3 ++- .../{weapon_flashlight.lua => weapon_flashlights.lua} | 0 .../{world_laser_colors.lua => world_lasers.lua} | 0 3 files changed, 2 insertions(+), 1 deletion(-) rename GoonMod/mods/custom_colours/{weapon_flashlight.lua => weapon_flashlights.lua} (100%) rename GoonMod/mods/custom_colours/{world_laser_colors.lua => world_lasers.lua} (100%) diff --git a/GoonMod/mod.txt b/GoonMod/mod.txt index 6d4fd7c..f70ea3e 100644 --- a/GoonMod/mod.txt +++ b/GoonMod/mod.txt @@ -68,6 +68,7 @@ { "hook_id" : "lib/managers/systemmenumanager", "script_path" : "goonbase.lua" }, { "hook_id" : "lib/managers/challengemanager", "script_path" : "goonbase.lua" }, { "hook_id" : "lib/managers/menu/playerprofileguiobject", "script_path" : "goonbase.lua" }, - { "hook_id" : "lib/managers/menu/walletguiobject", "script_path" : "goonbase.lua" } + { "hook_id" : "lib/managers/menu/walletguiobject", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/managers/experiencemanager", "script_path" : "goonbase.lua" } ] } diff --git a/GoonMod/mods/custom_colours/weapon_flashlight.lua b/GoonMod/mods/custom_colours/weapon_flashlights.lua similarity index 100% rename from GoonMod/mods/custom_colours/weapon_flashlight.lua rename to GoonMod/mods/custom_colours/weapon_flashlights.lua diff --git a/GoonMod/mods/custom_colours/world_laser_colors.lua b/GoonMod/mods/custom_colours/world_lasers.lua similarity index 100% rename from GoonMod/mods/custom_colours/world_laser_colors.lua rename to GoonMod/mods/custom_colours/world_lasers.lua From ad4f353439bf6c551899551d51c97b3f233a6650 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Fri, 20 Mar 2015 22:26:43 +1100 Subject: [PATCH 73/92] Mods menu will darken the background and remove the contract gui to make it easier to read Added ExperienceManager hooks --- GoonMod/lua/ExperienceManager.lua | 11 +++++++++ GoonMod/req/mods.lua | 41 ++++++++++++++++++++++++++++++- 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 GoonMod/lua/ExperienceManager.lua diff --git a/GoonMod/lua/ExperienceManager.lua b/GoonMod/lua/ExperienceManager.lua new file mode 100644 index 0000000..e72d1f8 --- /dev/null +++ b/GoonMod/lua/ExperienceManager.lua @@ -0,0 +1,11 @@ + +CloneClass( ExperienceManager ) + +Hooks:RegisterHook("ExperienceManagerGetRankString") +function ExperienceManager.rank_string(self, rank) + local r = Hooks:ReturnCall("ExperienceManagerGetRankString", self, rank) + if r ~= nil then + return r + end + return self.orig.rank_string(self, rank) +end diff --git a/GoonMod/req/mods.lua b/GoonMod/req/mods.lua index 9d62adb..09bb95f 100644 --- a/GoonMod/req/mods.lua +++ b/GoonMod/req/mods.lua @@ -68,13 +68,52 @@ Hooks:Add("MenuManagerBuildCustomMenus", "MenuManagerBuildCustomMenus_ModsMenu", if menu_nodes.main ~= nil or menu_nodes.lobby ~= nil then - menu_nodes[Mods.MenuID] = MenuHelper:BuildMenu( Mods.MenuID ) + MenuCallbackHandler.GoonModFocusModsMenu = function( node, focus ) + if focus then + Mods:ShowModManagerMenu() + else + Mods:HideModManagerMenu() + end + end + + local menu_data = { + focus_changed_callback = "GoonModFocusModsMenu" + } + menu_nodes[Mods.MenuID] = MenuHelper:BuildMenu( Mods.MenuID, menu_data ) Mods:VerifyAllRequirements() end end) +function Mods:ShowModManagerMenu() + + if not managers.menu_component or not managers.gui_data then + return + end + if managers.menu_component._contract_gui then + managers.menu_component:close_contract_gui() + end + + self._fullscreen_ws = self._fullscreen_ws or managers.gui_data:create_fullscreen_16_9_workspace() + if not self._darken_bg then + self._darken_bg = self._fullscreen_ws:panel():rect({ + color = Color.black:with_alpha(0.4), + layer = 50 + }) + end + self._darken_bg:set_alpha(1) + +end + +function Mods:HideModManagerMenu() + + if self._darken_bg then + self._darken_bg:set_alpha(0) + end + +end + function Mods:ShowHelpMenu() local title = managers.localization:text("gm_mods_info_popup_title") From e47f3032fc216487e989836656e7237917ea142a Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Fri, 20 Mar 2015 22:27:13 +1100 Subject: [PATCH 74/92] Fixed up hooks on custom flashlights and world lasers --- GoonMod/mods/custom_colours/weapon_flashlights.lua | 8 ++++---- GoonMod/mods/custom_colours/world_lasers.lua | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/GoonMod/mods/custom_colours/weapon_flashlights.lua b/GoonMod/mods/custom_colours/weapon_flashlights.lua index 85c17f4..0fde6c0 100644 --- a/GoonMod/mods/custom_colours/weapon_flashlights.lua +++ b/GoonMod/mods/custom_colours/weapon_flashlights.lua @@ -3,7 +3,7 @@ local Mod = class( BaseMod ) Mod.id = "CustomWeaponFlashlight" Mod.Name = "Custom Weapon Flashlight Colour" -Mod.Desc = "Set a custom colour for flashlights attached to your weapons" +Mod.Desc = "Modify the color of flashlights attached to your weapons" Mod.Requirements = {} Mod.Incompatibilities = {} Mod.Priority = 1 @@ -166,7 +166,7 @@ Hooks:Add("GameSetupUpdate", "GameSetupUpdate_" .. Mod:ID(), function(t, dt) end end) -Hooks:Add("WeaponFlashLightInit", "WeaponFlashLightInit_CustomLight", function(flashlight, unit) +Hooks:Add("WeaponFlashLightInit", "WeaponFlashLightInit_" .. Mod:ID(), function(flashlight, unit) if not Light:IsEnabled() then Hooks:Remove("WeaponFlashLightInit_CustomLight") @@ -177,7 +177,7 @@ Hooks:Add("WeaponFlashLightInit", "WeaponFlashLightInit_CustomLight", function(f end) -Hooks:Add("WeaponFlashLightCheckState", "WeaponFlashLightCheckState_CustomLight", function(flashlight) +Hooks:Add("WeaponFlashLightCheckState", "WeaponFlashLightCheckState_" .. Mod:ID(), function(flashlight) if flashlight._on then @@ -196,7 +196,7 @@ Hooks:Add("WeaponFlashLightCheckState", "WeaponFlashLightCheckState_CustomLight" end) -Hooks:Add("WeaponFlashLightUpdate", "WeaponFlashLightUpdate_Rainbow", function(flashlight, unit, t, dt) +Hooks:Add("WeaponFlashLightUpdate", "WeaponFlashLightUpdate_" .. Mod:ID(), function(flashlight, unit, t, dt) if not Light:IsEnabled() then return diff --git a/GoonMod/mods/custom_colours/world_lasers.lua b/GoonMod/mods/custom_colours/world_lasers.lua index c6dd51c..ede551c 100644 --- a/GoonMod/mods/custom_colours/world_lasers.lua +++ b/GoonMod/mods/custom_colours/world_lasers.lua @@ -3,7 +3,7 @@ local Mod = class( BaseMod ) Mod.id = "CustomWorldLaserColour" Mod.Name = "Custom World Laser Colour" -Mod.Desc = "Set a custom colour for lasers that appear in the game world" +Mod.Desc = "Modify the colour of lasers that appear in the game world" Mod.Requirements = {} Mod.Incompatibilities = {} Mod.Priority = 1 @@ -184,7 +184,7 @@ Hooks:Add("GameSetupUpdate", "GameSetupUpdate_" .. Mod:ID(), function(t, dt) end end) -Hooks:Add("ElementLaserTriggerPostInit", "ElementLaserTriggerPostInit_WorldLaser", function(laser) +Hooks:Add("ElementLaserTriggerPostInit", "ElementLaserTriggerPostInit_" .. Mod:ID(), function(laser) if not Lasers:IsEnabled() then return @@ -194,7 +194,7 @@ Hooks:Add("ElementLaserTriggerPostInit", "ElementLaserTriggerPostInit_WorldLaser end) -Hooks:Add("ElementLaserTriggerUpdateDraw", "ElementLaserTriggerUpdateDraw_WorldLaser", function(laser, t, dt) +Hooks:Add("ElementLaserTriggerUpdateDraw", "ElementLaserTriggerUpdateDraw_" .. Mod:ID(), function(laser, t, dt) if not Lasers:IsEnabled() then return From 7a9a7ba344e7f69faa3f526ed2332a261fb89419 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Sat, 21 Mar 2015 01:12:21 +1100 Subject: [PATCH 75/92] Reimplemented custom player and teammate lasers --- GoonMod/loc/en.txt | 11 + GoonMod/menus/custom_weapon_laser_menu.txt | 119 +++++ .../custom_colours/player_weapon_lasers.lua | 410 ++++++++++++++++++ GoonMod/mods/disabled/colors/weapon_laser.lua | 376 ---------------- 4 files changed, 540 insertions(+), 376 deletions(-) create mode 100644 GoonMod/menus/custom_weapon_laser_menu.txt create mode 100644 GoonMod/mods/custom_colours/player_weapon_lasers.lua delete mode 100644 GoonMod/mods/disabled/colors/weapon_laser.lua diff --git a/GoonMod/loc/en.txt b/GoonMod/loc/en.txt index ebe9519..0da57ee 100644 --- a/GoonMod/loc/en.txt +++ b/GoonMod/loc/en.txt @@ -260,6 +260,17 @@ "gm_options_cfc_enabled_title" : "Enable Custom Weapon Flashlight Color", "gm_options_cfc_enabled_desc" : "Use the custom set color for Weapon Flashlights", + "gm_options_cpwl_menu_title" : "Weapon Laser Colour", + "gm_options_cpwl_menu_desc" : "Modify the color of your own, and your teammates weapon lasers", + "gm_options_cpwl_enabled_title" : "Enable Custom Weapon Laser Colour", + "gm_options_cpwl_enabled_desc" : "Use the custom set color for lasers on your own weapons", + "gm_options_cpwl_teammate_lasers_title" : "Teammates Lasers", + "gm_options_cpwl_teammate_lasers_desc" : "Sets how teammate lasers appear in the world", + "gm_options_cpwl_teammate_lasers_off" : "Unchanged", + "gm_options_cpwl_teammate_lasers_same" : "Use My Colour", + "gm_options_cpwl_teammate_lasers_network" : "Networked Colour", + "gm_options_cpwl_teammate_lasers_unique" : "Unique per Person", + "gm_options_cwl_menu_title" : "World Laser Colour", "gm_options_cwl_menu_desc" : "Modify the color of lasers that appear in the world like on mines and sensors", "gm_options_cwl_enabled_title" : "Enable Custom World Laser Colour", diff --git a/GoonMod/menus/custom_weapon_laser_menu.txt b/GoonMod/menus/custom_weapon_laser_menu.txt new file mode 100644 index 0000000..63880c7 --- /dev/null +++ b/GoonMod/menus/custom_weapon_laser_menu.txt @@ -0,0 +1,119 @@ +{ + "menu_id" : "gm_options_cpwl_menu", + "parent_menu_id" : "goonbase_options_menu", + "title" : "gm_options_cpwl_menu_title", + "description" : "gm_options_cpwl_menu_desc", + "focus_changed_callback" : "CustomWeaponLaserMenuChangeFocus", + "back_callback" : "ClosedGoonModOptions", + "area_bg" : "half", + "items" : [ + + { + "type" : "toggle", + "id" : "gm_cpwl_toggle_custom_laser", + "title" : "gm_options_cpwl_enabled_title", + "description" : "gm_options_cpwl_enabled_desc", + "callback" : "ToggleEnableCustomWeaponLaser", + "value" : "Enabled", + "default_value" : true, + }, + { + "type" : "divider", + "size" : 8, + }, + + { + "type" : "toggle", + "id" : "gm_cpwl_toggle_custom_use_hue", + "title" : "gm_options_custom_use_hue_title", + "description" : "gm_options_custom_use_hue_desc", + "callback" : "CustomWeaponLaserToggleUseHue", + "value" : "UseHSV", + "default_value" : false, + }, + { + "type" : "slider", + "id" : "gm_cpwl_colour_slider_rh", + "title" : "gm_options_custom_rh_title", + "description" : "gm_options_custom_rh_desc", + "callback" : "CustomWeaponLaserSetRedHue", + "value" : "RH", + "default_value" : 1, + "min" : 0, + "max" : 1, + "step" : 0.01, + }, + { + "type" : "slider", + "id" : "gm_cpwl_colour_slider_gs", + "title" : "gm_options_custom_gs_title", + "description" : "gm_options_custom_gs_desc", + "callback" : "CustomWeaponLaserSetGreenSaturation", + "value" : "GS", + "default_value" : 1, + "min" : 0, + "max" : 1, + "step" : 0.01, + }, + { + "type" : "slider", + "id" : "gm_cpwl_colour_slider_bv", + "title" : "gm_options_custom_bv_title", + "description" : "gm_options_custom_bv_desc", + "callback" : "CustomWeaponLaserSetBlueValue", + "value" : "BV", + "default_value" : 1, + "min" : 0, + "max" : 1, + "step" : 0.01, + }, + { + "type" : "divider", + "size" : 64, + }, + + { + "type" : "toggle", + "id" : "gm_cpwl_toggle_rainbow", + "title" : "gm_options_custom_rainbow_title", + "description" : "gm_options_custom_rainbow_desc", + "callback" : "CustomWeaponLaserSetUseRainbow", + "value" : "UseRainbow", + "default_value" : false, + }, + { + "type" : "slider", + "id" : "gm_cpwl_slider_rainbow_speed", + "title" : "gm_options_custom_rainbow_speed_title", + "description" : "gm_options_custom_rainbow_speed_desc", + "callback" : "CustomWeaponLaserSetRainbowSpeed", + "value" : "RainbowSpeed", + "default_value" : 1, + "min" : 1, + "max" : 100, + "step" : 1, + }, + { + "type" : "divider", + "size" : 8, + }, + + { + "type" : "multiple_choice", + "id" : "json_menu_mutli", + "title" : "gm_options_cpwl_teammate_lasers_title", + "description" : "gm_options_cpwl_teammate_lasers_desc", + "callback" : "CustomWeaponLaserSetTeammateLaser", + "items" : [ + "gm_options_cpwl_teammate_lasers_off", + "gm_options_cpwl_teammate_lasers_same", + "gm_options_cpwl_teammate_lasers_network", + "gm_options_cpwl_teammate_lasers_unique" + ], + "value" : "TeamLasers", + "default_value" : 3, + } + + ] + +} diff --git a/GoonMod/mods/custom_colours/player_weapon_lasers.lua b/GoonMod/mods/custom_colours/player_weapon_lasers.lua new file mode 100644 index 0000000..a6cdf7a --- /dev/null +++ b/GoonMod/mods/custom_colours/player_weapon_lasers.lua @@ -0,0 +1,410 @@ + +-- Mod Definition +local Mod = class( BaseMod ) +Mod.id = "CustomWeaponLaserColour" +Mod.Name = "Custom Weapon Laser Colour" +Mod.Desc = "Modify the color of your own, and your teammates weapon lasers" +Mod.Requirements = {} +Mod.Incompatibilities = {} +Mod.Priority = 1 + +Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() + GoonBase.Mods:RegisterMod( Mod ) +end) + +if not Mod:IsEnabled() then + return +end + +-- Lasers +GoonBase.WeaponLasers = GoonBase.WeaponLasers or {} +local Lasers = GoonBase.WeaponLasers +Lasers.MenuFile = "custom_weapon_laser_menu.txt" +Lasers.NetworkID = "gmcpwlc" +Lasers._WorldOpacity = 0.4 + +-- Options +GoonBase.Options.WeaponLasers = GoonBase.Options.WeaponLasers or {} +GoonBase.Options.WeaponLasers.Enabled = GoonBase.Options.WeaponLasers.Enabled or true +GoonBase.Options.WeaponLasers.RH = GoonBase.Options.WeaponLasers.RH or 0.0 +GoonBase.Options.WeaponLasers.GS = GoonBase.Options.WeaponLasers.GS or 0.75 +GoonBase.Options.WeaponLasers.BV = GoonBase.Options.WeaponLasers.BV or 0.0 +GoonBase.Options.WeaponLasers.UseHSV = GoonBase.Options.WeaponLasers.UseHSV or false +GoonBase.Options.WeaponLasers.UseRainbow = GoonBase.Options.WeaponLasers.UseRainbow or false +GoonBase.Options.WeaponLasers.RainbowSpeed = GoonBase.Options.WeaponLasers.RainbowSpeed or 1 +GoonBase.Options.WeaponLasers.TeamLasers = GoonBase.Options.WeaponLasers.TeamLasers or 3 + +-- Laser Colours +Lasers.Color = ColorHSVRGB:new( GoonBase.Options.WeaponLasers, Color.red:with_alpha(0.6) ) +Lasers.TeammateColours = Lasers.TeammateColours or {} +Lasers.UniquePlayerColours = Lasers.UniquePlayerColours or { + [1] = Color("29ce31"), + [2] = Color("00eae8"), + [3] = Color("f99d1c"), + [4] = Color("ebe818"), + [5] = Color("ebe818"), +} + +-- Functions +function Lasers:IsEnabled() + return GoonBase.Options.WeaponLasers.Enabled +end + +function Lasers:IsRainbow() + return GoonBase.Options.UseRainbow.Rainbow +end + +function Lasers:GetColor(alpha) + if Lasers.Color == nil then + Lasers.Color = ColorHSVRGB:new() + Lasers.Color:SetOptionsTable( "WeaponLasers" ) + end + return Lasers.Color:GetColor( alpha ) or Color( alpha or 1, 1, 0, 0 ) +end + + +function Lasers:IsEnabled() + return GoonBase.Options.WeaponLasers.Enabled +end + +function Lasers:IsRainbow() + return GoonBase.Options.WeaponLasers.UseRainbow +end + +function Lasers:RainbowSpeed() + return GoonBase.Options.WeaponLasers.RainbowSpeed +end + +function Lasers:GetColor(alpha) + return Lasers.Color:GetColor( alpha ) +end + +function Lasers:AreTeamLasersOff() + return GoonBase.Options.WeaponLasers.TeamLasers == 1 +end + +function Lasers:AreTeamLasersSameColour() + return GoonBase.Options.WeaponLasers.TeamLasers == 2 +end + +function Lasers:AreTeamLasersNetworked() + return GoonBase.Options.WeaponLasers.TeamLasers == 3 +end + +function Lasers:AreTeamLasersUnique() + return GoonBase.Options.WeaponLasers.TeamLasers == 4 +end + +function Lasers:GetCriminalNameFromLaserUnit( laser ) + + if not self._laser_units_lookup then + self._laser_units_lookup = {} + end + + local laser_key = nil + if laser._unit then + laser_key = laser._unit:key() + end + if laser_key and self._laser_units_lookup[laser_key] ~= nil then + return self._laser_units_lookup[laser_key] + end + + local criminals_manager = managers.criminals + if not criminals_manager then + return + end + + for id, character in pairs(criminals_manager._characters) do + if alive(character.unit) and character.unit:inventory() and character.unit:inventory():equipped_unit() then + + local weapon_base = character.unit:inventory():equipped_unit():base() + if Lasers:CheckWeaponForLasers( weapon_base, laser_key ) then + self._laser_units_lookup[laser_key] = character.name + return + end + + if weapon_base._second_gun then + if Lasers:CheckWeaponForLasers( weapon_base._second_gun:base(), laser_key ) then + self._laser_units_lookup[laser_key] = character.name + return + end + end + + end + end + + if laser_key then + self._laser_units_lookup[laser_key] = false + end + return nil + +end + +function Lasers:CheckWeaponForLasers( weapon_base, key ) + + if weapon_base and weapon_base._factory_id and weapon_base._blueprint then + + local gadgets = managers.weapon_factory:get_parts_from_weapon_by_type_or_perk("gadget", weapon_base._factory_id, weapon_base._blueprint) + if gadgets then + for _, i in ipairs(gadgets) do + + local gadget_key = weapon_base._parts[i].unit:key() + if gadget_key == key then + return true + end + + end + end + + end + return false + +end + +function Lasers:UpdateLaser( laser, unit, t, dt ) + + if not Lasers:IsEnabled() then + return + end + + if laser._is_npc then + + local criminal_name = Lasers:GetCriminalNameFromLaserUnit( laser ) + if not criminal_name then + return + end + + if Lasers:AreTeamLasersOff() then + Lasers:SetColourOfLaser( laser, unit, t, dt, Color.green:with_alpha(0.1) ) + return + end + + if Lasers:AreTeamLasersSameColour() then + Lasers:SetColourOfLaser( laser, unit, t, dt ) + return + end + + if Lasers:AreTeamLasersNetworked() then + local color = Lasers.TeammateColours[criminal_name] + if color then + Lasers:SetColourOfLaser( laser, unit, t, dt, color ) + end + return + end + + if Lasers:AreTeamLasersUnique() then + local id = managers.criminals:character_color_id_by_name( criminal_name ) + if id == 1 then id = id + 1 end + local color = Lasers.UniquePlayerColours[ id or 5 ]:with_alpha(Lasers._WorldOpacity) + if color then + Lasers:SetColourOfLaser( laser, unit, t, dt, color ) + end + return + end + + end + + Lasers:SetColourOfLaser( laser, unit, t, dt ) + +end + +function Lasers:SetColourOfLaser( laser, unit, t, dt, override_color ) + + if override_color then + if override_color ~= "rainbow" then + laser:set_color( override_color ) + else + if type(override_color) == "string" then + laser:set_color_by_theme( override_color ) + else + laser:set_color( Lasers.Color:GetRainbowColor( t, Lasers:RainbowSpeed() ):with_alpha(Lasers._WorldOpacity) ) + end + end + return + end + + if not Lasers:IsRainbow() then + laser:set_color( Lasers:GetColor( Lasers._WorldOpacity ) ) + return + end + + if Lasers:IsRainbow() then + laser:set_color( Lasers.Color:GetRainbowColor( t, Lasers:RainbowSpeed() ):with_alpha(Lasers._WorldOpacity) ) + return + end + +end + + +function Lasers:ShowPreviewMenuItem() + + if not managers.menu_component then + return + end + + local ws = managers.menu_component._ws + self._panel = ws:panel():panel() + + local w, h = self._panel:w() * 0.35, 48 + self._color_rect = self._panel:rect({ + w = w, + h = h, + color = Color.red, + blend_mode = "add", + layer = tweak_data.gui.MOUSE_LAYER - 50, + }) + self._color_rect:set_right( self._panel:right() ) + self._color_rect:set_top( self._panel:h() * 0.265 ) + + self:UpdatePreview() + +end + +function Lasers:DestroyPreviewMenuItem() + + if alive(self._panel) then + + self._panel:remove( self._color_rect ) + self._panel:remove( self._panel ) + + self._color_rect = nil + self._panel = nil + + end + +end + +function Lasers:UpdatePreview( t ) + + if not alive(self._panel) or not alive(self._color_rect) or not Lasers.Color then + return + end + + if self:IsRainbow() and t then + self._color_rect:set_color( Lasers.Color:GetRainbowColor(t, self:RainbowSpeed()) ) + else + self._color_rect:set_color( Lasers.Color:GetColor() ) + end + +end + +-- Menu +Hooks:Add("MenuManagerInitialize", "MenuManagerInitialize_" .. Mod:ID(), function( menu_manager ) + + -- Callbacks + MenuCallbackHandler.CustomWeaponLaserMenuChangeFocus = function( node, focus ) + if focus then + Lasers:ShowPreviewMenuItem() + else + Lasers:DestroyPreviewMenuItem() + end + end + + MenuCallbackHandler.ToggleEnableCustomWeaponLaser = function( this, item ) + GoonBase.Options.WeaponLasers.Enabled = item:value() == "on" and true or false + Lasers:UpdatePreview() + end + + MenuCallbackHandler.CustomWeaponLaserToggleUseHue = function( this, item ) + GoonBase.Options.WeaponLasers.UseHSV = item:value() == "on" and true or false + Lasers:UpdatePreview() + end + + MenuCallbackHandler.CustomWeaponLaserSetRedHue = function( this, item ) + GoonBase.Options.WeaponLasers.RH = tonumber( item:value() ) + Lasers:UpdatePreview() + end + + MenuCallbackHandler.CustomWeaponLaserSetGreenSaturation = function( this, item ) + GoonBase.Options.WeaponLasers.GS = tonumber( item:value() ) + Lasers:UpdatePreview() + end + + MenuCallbackHandler.CustomWeaponLaserSetBlueValue = function( this, item ) + GoonBase.Options.WeaponLasers.BV = tonumber( item:value() ) + Lasers:UpdatePreview() + end + + MenuCallbackHandler.CustomWeaponLaserSetUseRainbow = function( this, item ) + GoonBase.Options.WeaponLasers.UseRainbow = item:value() == "on" and true or false + Lasers:UpdatePreview() + end + + MenuCallbackHandler.CustomWeaponLaserSetRainbowSpeed = function( this, item ) + GoonBase.Options.WeaponLasers.RainbowSpeed = tonumber( item:value() ) + end + + MenuCallbackHandler.CustomWeaponLaserSetTeammateLaser = function( this, item ) + GoonBase.Options.WeaponLasers.TeamLasers = tonumber( item:value() ) + end + + MenuHelper:LoadFromJsonFile( GoonBase.MenusPath .. Lasers.MenuFile, GoonBase.WeaponLasers, GoonBase.Options.WeaponLasers ) + +end) + +-- Hooks +Hooks:Add("MenuUpdate", "MenuUpdate_" .. Mod:ID(), function(t, dt) + if Lasers:IsRainbow() then + Lasers:UpdatePreview( t ) + end +end) + +Hooks:Add("GameSetupUpdate", "GameSetupUpdate_" .. Mod:ID(), function(t, dt) + if Lasers:IsRainbow() then + Lasers:UpdatePreview( t ) + end +end) + +Hooks:Add("WeaponLaserInit", "WeaponLaserInit_" .. Mod:ID(), function(laser, unit) + Lasers:UpdateLaser(laser, unit, 0, 0) +end) + +Hooks:Add("WeaponLaserUpdate", "WeaponLaserUpdate_Rainbow_" .. Mod:ID(), function(laser, unit, t, dt) + Lasers:UpdateLaser(laser, unit, t, dt) +end) + +Hooks:Add("WeaponLaserSetOn", "WeaponLaserSetOn_" .. Mod:ID(), function(laser) + + if laser._is_npc then + return + end + + local criminals_manager = managers.criminals + if not criminals_manager then + return + end + + local local_name = criminals_manager:local_character_name() + local laser_name = Lasers:GetCriminalNameFromLaserUnit( laser ) + if laser_name == nil or local_name == laser_name then + local col_str = LuaNetworking:ColourToString( Lasers:GetColor() ) + if Lasers:IsRainbow() then + col_str = "rainbow" + end + LuaNetworking:SendToPeers( Lasers.NetworkID, col_str ) + end + +end) + +Hooks:Add("NetworkReceivedData", "NetworkReceivedData_" .. Mod:ID(), function(sender, message, data) + + if message == Lasers.NetworkID then + + local criminals_manager = managers.criminals + if not criminals_manager then + return + end + + local char = criminals_manager:character_name_by_peer_id(sender) + local col = data + if data ~= "rainbow" then + col = LuaNetworking:StringToColour(data) + end + + if char then + Lasers.TeammateColours[char] = col + end + + end + +end) diff --git a/GoonMod/mods/disabled/colors/weapon_laser.lua b/GoonMod/mods/disabled/colors/weapon_laser.lua deleted file mode 100644 index 0897efb..0000000 --- a/GoonMod/mods/disabled/colors/weapon_laser.lua +++ /dev/null @@ -1,376 +0,0 @@ - --- Mod Definition -local Mod = class( BaseMod ) -Mod.id = "CustomWeaponLaser" -Mod.Name = "Custom Weapon Laser Colour" -Mod.Desc = "Set a custom colour for lasers attached to your weapons" -Mod.Requirements = {} -Mod.Incompatibilities = {} -Mod.Priority = 1 - -Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() - GoonBase.Mods:RegisterMod( Mod ) -end) - -if not Mod:IsEnabled() then - return -end - --- Weapon Laser -GoonBase.WeaponLaser = GoonBase.WeaponLaser or {} -local Laser = GoonBase.WeaponLaser -Laser.MenuId = "goonbase_weapon_laser_menu" -Laser.Color = nil -Laser.UniquePlayerColours = { - [1] = Color("29ce31"), -- MrGreen - [2] = Color("00eae8"), -- MrBlue - [3] = Color("f99d1c"), -- MrBrown - [4] = Color("ebe818"), -- MrOrange - [5] = Color("ebe818"), -- MrAI -} -Laser.OtherColours = {} - --- Networking -Laser.Network = Laser.Network or {} -local Network = Laser.Network -Network.SendLaserColour = "CustomLaserColour" - --- Options -if GoonBase.Options.WeaponLaser == nil then - GoonBase.Options.WeaponLaser = {} - GoonBase.Options.WeaponLaser.Enabled = false - GoonBase.Options.WeaponLaser.R = 1 - GoonBase.Options.WeaponLaser.G = 0.1 - GoonBase.Options.WeaponLaser.B = 0.1 - GoonBase.Options.WeaponLaser.HSV = false - GoonBase.Options.WeaponLaser.Rainbow = false - GoonBase.Options.WeaponLaser.RainbowSpeed = 10 - GoonBase.Options.WeaponLaser.TeammateLasers = 1 -end - --- Functions -function Laser:IsEnabled() - return GoonBase.Options.WeaponLaser.Enabled -end - -function Laser:IsRainbow() - return GoonBase.Options.WeaponLaser.Rainbow -end - -function Laser:GetColor( alpha ) - if Laser.Color == nil then - Laser.Color = ColorHSVRGB:new() - Laser.Color:SetOptionsTable( "WeaponLaser" ) - end - return Laser.Color:GetColor( alpha ) or Color( alpha or 1, 1, 0, 0 ) -end - -function Laser:GetCriminalNameFromLaserUnit( laser ) - - if not self._laser_units_lookup then - self._laser_units_lookup = {} - end - - local laser_key = nil - if laser._unit then - laser_key = laser._unit:key() - end - if laser_key and self._laser_units_lookup[laser_key] ~= nil then - return self._laser_units_lookup[laser_key] - end - - local criminals_manager = managers.criminals - if not criminals_manager then - return - end - - for id, data in pairs(criminals_manager._characters) do - if data.unit ~= nil and alive(data.unit) and data.name ~= criminals_manager:local_character_name() then - - if data.unit:inventory() and data.unit:inventory():equipped_unit() then - - local wep_base = data.unit:inventory():equipped_unit():base() - if wep_base then - - if wep_base._factory_id ~= nil and wep_base._blueprint ~= nil then - - local gadgets = managers.weapon_factory:get_parts_from_weapon_by_type_or_perk("gadget", wep_base._factory_id, wep_base._blueprint) - if gadgets then - local gadget - for _, i in ipairs(gadgets) do - - gadget = wep_base._parts[i] - gadget = gadget.unit:base() - - if gadget == laser then - if laser_key then - self._laser_units_lookup[laser_key] = data.name - end - return data.name - end - - end - end - - end - - end - - end - - end - end - - if laser_key then - self._laser_units_lookup[laser_key] = false - end - return nil - -end - -function Laser:UsingSameColour() - return GoonBase.Options.WeaponLaser.TeammateLasers == 1 -end - -function Laser:UsingUniqueColour() - return GoonBase.Options.WeaponLaser.TeammateLasers == 2 -end - -function Laser:UsingPlayerColour() - return GoonBase.Options.WeaponLaser.TeammateLasers == 3 -end - --- Menu -Hooks:Add("MenuManagerSetupCustomMenus", "MenuManagerSetupCustomMenus_" .. Mod:ID(), function(menu_manager, menu_nodes) - MenuHelper:NewMenu( Laser.MenuId ) -end) - -Hooks:Add("MenuManagerSetupGoonBaseMenu", "MenuManagerSetupGoonBaseMenu_" .. Mod:ID(), function(menu_manager, menu_nodes) - - -- Submenu Button - MenuHelper:AddButton({ - id = "weapon_laser_button", - title = "Options_WeaponLaserName", - desc = "Options_WeaponLaserDesc", - next_node = Laser.MenuId, - menu_id = "goonbase_options_menu", - }) - - -- Enabled Toggle - MenuCallbackHandler.toggle_custom_weapon_laser_color = function(this, item) - GoonBase.Options.WeaponLaser.Enabled = item:value() == "on" and true or false - GoonBase.Options:Save() - end - - MenuHelper:AddToggle({ - id = "toggle_custom_weapon_laser_color", - title = "Options_WeaponLaserEnableTitle", - desc = "Options_WeaponLaserEnableDesc", - callback = "toggle_custom_weapon_laser_color", - value = GoonBase.Options.WeaponLaser.Enabled, - menu_id = Laser.MenuId, - priority = 50 - }) - - -- RGB/HSV Colour - Laser.Color = ColorHSVRGB:new() - Laser.Color:SetID( "weapon_laser" ) - Laser.Color:SetPriority( 30 ) - Laser.Color:SetOptionsTable( "WeaponLaser" ) - Laser.Color:SetupMenu( Laser.MenuId ) - - -- Rainbow Laser - MenuCallbackHandler.toggle_weapon_laser_rainbow = function(this, item) - GoonBase.Options.WeaponLaser.Rainbow = item:value() == "on" and true or false - GoonBase.Options:Save() - end - - MenuCallbackHandler.weapon_laser_rainbow_speed = function(this, item) - GoonBase.Options.WeaponLaser.RainbowSpeed = item:value() - GoonBase.Options:Save() - end - - MenuCallbackHandler.weapon_laser_set_teammate = function(this, item) - GoonBase.Options.WeaponLaser.TeammateLasers = tonumber(item:value()) - GoonBase.Options:Save() - end - - MenuHelper:AddToggle({ - id = "toggle_weapon_laser_rainbow", - title = "Options_WeaponLaserRainbowTitle", - desc = "Options_WeaponLaserRainbowDesc", - callback = "toggle_weapon_laser_rainbow", - value = GoonBase.Options.WeaponLaser.Rainbow, - menu_id = Laser.MenuId, - priority = 25 - }) - - MenuHelper:AddSlider({ - id = "weapon_laser_rainbow_speed", - title = "Options_WeaponLaserRainbowSpeedTitle", - desc = "Options_WeaponLaserRainbowSpeedDesc", - callback = "weapon_laser_rainbow_speed", - value = GoonBase.Options.WeaponLaser.RainbowSpeed, - min = 1, - max = 100, - step = 1, - show_value = true, - menu_id = Laser.MenuId, - priority = 24, - }) - - MenuHelper:AddDivider({ - id = "weapon_laser_divider", - menu_id = Laser.MenuId, - size = 16, - priority = 21, - }) - - MenuHelper:AddMultipleChoice({ - id = "weapon_laser_teammate_choice", - title = "Options_TeammateLaserOption", - desc = "Options_TeammateLaserOptionDesc", - callback = "weapon_laser_set_teammate", - menu_id = Laser.MenuId, - priority = 20, - items = { - [1] = "Options_TeammateLaser_Same", - [2] = "Options_TeammateLaser_Unique", - [3] = "Options_TeammateLaser_Theirs", - }, - value = GoonBase.Options.WeaponLaser.TeammateLasers, - }) - -end) - -Hooks:Add("MenuManagerBuildCustomMenus", "MenuManagerBuildCustomMenus_" .. Mod:ID(), function(menu_manager, mainmenu_nodes) - local menu_id = Laser.MenuId - local data = { - area_bg = "half" - } - mainmenu_nodes[menu_id] = MenuHelper:BuildMenu( menu_id, data ) -end) - --- Hooks -Hooks:Add("WeaponLaserInit", "WeaponLaserInit_" .. Mod:ID(), function(laser, unit) - Laser:UpdateLaser(laser, unit, 0, 0) -end) - -Hooks:Add("WeaponLaserUpdate", "WeaponLaserUpdate_Rainbow_" .. Mod:ID(), function(laser, unit, t, dt) - Laser:UpdateLaser(laser, unit, t, dt) -end) - -function Laser:UpdateLaser( laser, unit, t, dt ) - - local psuccess, perror = pcall(function() - - if not Laser:IsEnabled() then - return - end - - if laser._is_npc then - - local criminal_name = Laser:GetCriminalNameFromLaserUnit( laser ) - if not criminal_name then - return - end - - if Laser:UsingSameColour() then - Laser:SetColourOfLaser( laser, unit, t, dt ) - end - - if Laser:UsingUniqueColour() then - local id = managers.criminals:character_color_id_by_name( criminal_name ) - if id == 1 then id = id + 1 end - local col = Laser.UniquePlayerColours[ id or 5 ] - Laser:SetColourOfLaser( laser, unit, t, dt, col ) - end - - if Laser:UsingPlayerColour() then - local col = Laser.OtherColours[criminal_name] - Laser:SetColourOfLaser( laser, unit, t, dt, col ) - end - - return - - end - - Laser:SetColourOfLaser( laser, unit, t, dt ) - - end) - if not psuccess then - Print("[Error] " .. perror) - end - -end - -function Laser:SetColourOfLaser( laser, unit, t, dt, colour_override ) - - if colour_override ~= nil and colour_override ~= "rainbow" then - local psuccess, perror = pcall(function() - laser:set_color( colour_override:with_alpha(0.4) ) - end) - if not psuccess then - Print("[Error] " .. perror) - end - return - end - - if not Laser:IsRainbow() then - laser:set_color( Laser:GetColor(0.4) ) - end - - if Laser:IsRainbow() or colour_override == "rainbow" then - Laser:GetColor() - local r, g, b = Laser.Color:ToRGB( math.sin(GoonBase.Options.WeaponLaser.RainbowSpeed * t), GoonBase.Options.WeaponLaser.G, GoonBase.Options.WeaponLaser.B ) - laser:set_color( Color(r, g, b):with_alpha(0.4) ) - end - -end - --- Networked Colour -Hooks:Add("WeaponLaserSetOn", "WeaponLaserSetOn_" .. Mod:ID(), function(laser) - - if laser._is_npc then - return - end - - local criminals_manager = managers.criminals - if not criminals_manager then - return - end - - local local_name = criminals_manager:local_character_name() - local laser_name = Laser:GetCriminalNameFromLaserUnit( laser ) - if laser_name == nil or local_name == laser_name then - local col_str = LuaNetworking:PrepareNetworkedColourString( Laser:GetColor() ) - if Laser:IsRainbow() then - col_str = "rainbow" - end - LuaNetworking:SendToPeers( Network.SendLaserColour, col_str ) - end - -end) - -Hooks:Add("NetworkReceivedData", "NetworkReceivedData_" .. Mod:ID(), function(sender, message, data) - - if message == Network.SendLaserColour then - - local criminals_manager = managers.criminals - if not criminals_manager then - return - end - - local char = criminals_manager:character_name_by_peer_id(sender) - local col = data - if data ~= "rainbow" then - col = LuaNetworking:NetworkedColourStringToColour(data) - end - - if char then - Laser.OtherColours[char] = col - end - - end - -end) From 2fc68fbc63e34a56c7ac1924c6a2dde942902728 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Sun, 22 Mar 2015 16:18:25 +1100 Subject: [PATCH 76/92] Added Infamy Outliner mod Added SkillTreeGUI and SkillTreeManager hooks Moved ColorHSVRGB to req folder --- GoonMod/goonbase.lua | 3 + GoonMod/loc/en.txt | 10 + GoonMod/lua/CriminalsManager.lua | 3 +- GoonMod/lua/ECMJammerBase.lua | 144 --------- GoonMod/lua/NetworkGame.lua | 18 +- GoonMod/lua/SkillTreeGUI.lua | 14 + GoonMod/lua/SkillTreeManager.lua | 8 + GoonMod/menus/infamy_outliner_menu.txt | 84 +++++ GoonMod/mod.txt | 5 +- GoonMod/mods/infamy_outliner.lua | 294 ++++++++++++++++++ GoonMod/mods/zoom_sensitivity.lua | 2 +- .../custom_colours => req}/color_hsvrgb.lua | 0 12 files changed, 426 insertions(+), 159 deletions(-) create mode 100644 GoonMod/lua/SkillTreeGUI.lua create mode 100644 GoonMod/lua/SkillTreeManager.lua create mode 100644 GoonMod/menus/infamy_outliner_menu.txt create mode 100644 GoonMod/mods/infamy_outliner.lua rename GoonMod/{mods/custom_colours => req}/color_hsvrgb.lua (100%) diff --git a/GoonMod/goonbase.lua b/GoonMod/goonbase.lua index a4c1706..dcd623d 100644 --- a/GoonMod/goonbase.lua +++ b/GoonMod/goonbase.lua @@ -83,6 +83,9 @@ GoonBase.HookFiles = { ["lib/managers/menu/playerprofileguiobject"] = "PlayerProfileGUIObject.lua", ["lib/managers/menu/walletguiobject"] = "WalletGUIObject.lua", ["lib/managers/experiencemanager"] = "ExperienceManager.lua", + ["lib/managers/skilltreemanager"] = "SkillTreeManager.lua", + ["lib/managers/menu/skilltreegui"] = "SkillTreeGUI.lua", + ["lib/network/networkgame"] = "NetworkGame.lua", } diff --git a/GoonMod/loc/en.txt b/GoonMod/loc/en.txt index 0da57ee..9fba832 100644 --- a/GoonMod/loc/en.txt +++ b/GoonMod/loc/en.txt @@ -290,4 +290,14 @@ "Options_TeammateLaser_Theirs" : "Use Their Colour", "Options_TeammateLaser_Unique" : "Unique per Person", + "gm_options_choose_any_character_title" : "Enable Multiple Characters", + "gm_options_choose_any_character_desc" : "Allow players to join as any character they want, even with multiple of the same character", + + "gm_options_infamy_outliner_menu_title" : "Infamy Outliner", + "gm_options_infamy_outliner_menu_desc" : "Customize the colour and manage your saved infamous skill tree data", + "gm_options_infamy_outliner_enabled_title" : "Enable Infamy Outliner", + "gm_options_infamy_outliner_enabled_desc" : "Show the infamy outliner on the skill tree screen", + "gm_options_infamy_outliner_clear_data_title" : "Clear Infamy Data", + "gm_options_infamy_outliner_clear_data_desc" : "Clear Infamy Outliner Data", + } diff --git a/GoonMod/lua/CriminalsManager.lua b/GoonMod/lua/CriminalsManager.lua index 034712b..d3da342 100644 --- a/GoonMod/lua/CriminalsManager.lua +++ b/GoonMod/lua/CriminalsManager.lua @@ -3,10 +3,9 @@ CloneClass( CriminalsManager ) Hooks:Call("CriminalsManagerNumberOfTakenCriminals") function CriminalsManager.nr_taken_criminals(self) - local orig = self.orig.nr_taken_criminals(self) local r = Hooks:ReturnCall("CriminalsManagerNumberOfTakenCriminals", self) if r ~= nil then return r end - return orig + return self.orig.nr_taken_criminals(self) end diff --git a/GoonMod/lua/ECMJammerBase.lua b/GoonMod/lua/ECMJammerBase.lua index a64fc80..7671b0c 100644 --- a/GoonMod/lua/ECMJammerBase.lua +++ b/GoonMod/lua/ECMJammerBase.lua @@ -1,146 +1,2 @@ CloneClass( ECMJammerBase ) - -function ECMJammerBase:init(unit) - - UnitBase.init(self, unit, true) - - self._unit = unit - self._position = self._unit:position() - self._rotation = self._unit:rotation() - self._g_glow_jammer_green = self._unit:get_object(Idstring("g_glow_func1_green")) - self._g_glow_jammer_red = self._unit:get_object(Idstring("g_glow_func1_red")) - self._g_glow_feedback_green = self._unit:get_object(Idstring("g_glow_func2_green")) - self._g_glow_feedback_red = self._unit:get_object(Idstring("g_glow_func2_red")) - self._max_battery_life = tweak_data.upgrades.ecm_jammer_base_battery_life - self._battery_life = 0.01 - self._low_battery_life = tweak_data.upgrades.ecm_jammer_base_low_battery_life - self._feedback_active = false - self._jammer_active = false - - if Network:is_client() then - self._validate_clbk_id = "ecm_jammer_validate" .. tostring(unit:key()) - managers.enemy:add_delayed_clbk(self._validate_clbk_id, callback(self, self, "_clbk_validate"), Application:time() + 60) - end - -end - -function ECMJammerBase:setup(battery_life_upgrade_lvl, owner) - - self._slotmask = managers.slot:get_mask("trip_mine_targets") - self._max_battery_life = tweak_data.upgrades.ecm_jammer_base_battery_life * battery_life_upgrade_lvl - self._battery_life = 0.01 - self._owner = owner - self._owner_id = owner and managers.network:game():member_from_unit(owner):peer():id() - - self:spawn_workspace() - -end - -function ECMJammerBase:spawn_workspace() - - local w, h = 320, 320 - local scale = 1 - local scaled_w, scaled_h = w * scale, h * scale - local pos, rot = self._position + Vector3((scaled_w / 2), -(scaled_h / 2), 1), self._rotation - - local new_gui = World:newgui() - self._workspace = new_gui:create_world_workspace(w, h, pos, Vector3(-scaled_w, 0, 0):rotate_with(rot), Vector3(0, 0, -scaled_h):rotate_with(rot)) - - self._circle_1 = CircleBitmapGuiObject:new(self._workspace:panel(), { - use_bg = false, - radius = self._workspace:panel():w() / 2, - sides = 64, - current = 64, - total = 64, - color = Color.white, - alpha = 1, - blend_mode = "add", - image = "guis/textures/pd2/specialization/progress_ring", - layer = 2 - }) - self._circle_1:set_current( 0.66 ) - self._circle_1_rotation = math.random(0, 360) - self._circle_1._circle:set_rotation( self._circle_1_rotation ) - -end - -function ECMJammerBase:set_battery_low() -end - -function ECMJammerBase:set_feedback_active() - - if not managers.network:session() then - return - end - - if not ECMJammerBase._teleport_destination then - ECMJammerBase._teleport_destination = self._position - else - self:teleport() - end - -end - -function ECMJammerBase:teleport() - - local hit_pos = self._position - local range = 400 - local slotmask = managers.slot:get_mask("explosion_targets") - local bodies = World:find_bodies("intersect", "sphere", hit_pos, range, slotmask) - - if not self._teleported_units then - self._teleported_units = {} - end - - for _, body in ipairs(bodies) do - local character = body:unit() - if character:carry_data() and body:collisions_enabled() then - - body:set_keyframed() - -- body:set_collisions_enabled(false) - character:set_position( ECMJammerBase._teleport_destination + Vector3(0, 0, 100) ) - - table.insert( self._teleported_units, character ) - - end - end - self._teleported_units_time = Application:time() - - ECMJammerBase._teleport_destination = nil - -end - -function ECMJammerBase:update(unit, t, dt) - - self.orig.update(self, unit, t, dt) - - self:update_teleported_units(unit, t, dt) - self:update_rotations(unit, t, dt) - -end - -function ECMJammerBase:update_teleported_units(unit, t, dt) - - if self._teleported_units and Application:time() - self._teleported_units_time > 0.5 then - - local bounds = 1000000 - for k, v in pairs( self._teleported_units ) do - v:set_dynamic() - v:set_collisions_enabled(true) - v:push(100, math.UP * 600) - end - self._teleported_units = nil - - end - -end - -function ECMJammerBase:update_rotations(unit, t, dt) - - if self._circle_1 then - self._circle_1_rotation = self._circle_1_rotation + dt * 10 - self._circle_1._circle:set_rotation( self._circle_1_rotation ) - end - -end diff --git a/GoonMod/lua/NetworkGame.lua b/GoonMod/lua/NetworkGame.lua index 6d88cc1..001e0d1 100644 --- a/GoonMod/lua/NetworkGame.lua +++ b/GoonMod/lua/NetworkGame.lua @@ -3,19 +3,15 @@ CloneClass( NetworkGame ) Hooks:RegisterHook("NetworkGamePostLoad") function NetworkGame.load(self, game_data) - Print("NetworkGame.load()") self.orig.load(self, game_data) Hooks:Call("NetworkGamePostLoad", self, game_data) end -function NetworkGame.on_network_started(self) - Print("NetworkGame.on_network_started()") - self.orig.on_network_started(self) -end - - -function NetworkGame.init(self) - Print("NetworkGame.init()") - self.orig.init(self) - Print("network manager: ", managers.network) +Hooks:RegisterHook("NetworkGameCheckPeerPreferredCharacter") +function NetworkGame.check_peer_preferred_character(self, preferred_character) + local r = Hooks:ReturnCall("NetworkGameCheckPeerPreferredCharacter", self, preferred_character) + if r ~= nil then + return r + end + return self.orig.check_peer_preferred_character(self, preferred_character) end diff --git a/GoonMod/lua/SkillTreeGUI.lua b/GoonMod/lua/SkillTreeGUI.lua new file mode 100644 index 0000000..f68bdc7 --- /dev/null +++ b/GoonMod/lua/SkillTreeGUI.lua @@ -0,0 +1,14 @@ + +CloneClass( SkillTreeSkillItem ) + +Hooks:RegisterHook("SkillTreeSkillItemPostInit") +function SkillTreeSkillItem.init(self, skill_id, tier_panel, num_skills, i, tree, tier, w, h, skill_refresh_skills) + self.orig.init(self, skill_id, tier_panel, num_skills, i, tree, tier, w, h, skill_refresh_skills) + Hooks:Call("SkillTreeSkillItemPostInit", self, skill_id, tier_panel, num_skills, i, tree, tier, w, h, skill_refresh_skills) +end + +Hooks:RegisterHook("SkillTreeSkillItemPostRefresh") +function SkillTreeSkillItem.refresh(self, locked) + self.orig.refresh(self, locked) + Hooks:Call("SkillTreeSkillItemPostRefresh", self, locked) +end diff --git a/GoonMod/lua/SkillTreeManager.lua b/GoonMod/lua/SkillTreeManager.lua new file mode 100644 index 0000000..c0474c0 --- /dev/null +++ b/GoonMod/lua/SkillTreeManager.lua @@ -0,0 +1,8 @@ + +CloneClass( SkillTreeManager ) + +Hooks:RegisterHook("SkillTreeManagerPreInfamyReset") +function SkillTreeManager.infamy_reset(self) + Hooks:Call("SkillTreeManagerPreInfamyReset", self) + self.orig.infamy_reset(self) +end diff --git a/GoonMod/menus/infamy_outliner_menu.txt b/GoonMod/menus/infamy_outliner_menu.txt new file mode 100644 index 0000000..5e006ad --- /dev/null +++ b/GoonMod/menus/infamy_outliner_menu.txt @@ -0,0 +1,84 @@ +{ + "menu_id" : "gm_options_io_menu", + "parent_menu_id" : "goonbase_options_menu", + "title" : "gm_options_infamy_outliner_menu_title", + "description" : "gm_options_infamy_outliner_menu_desc", + "focus_changed_callback" : "InfamyOutlinerChangedFocus", + "back_callback" : "ClosedGoonModOptions", + "items" : [ + + { + "type" : "toggle", + "id" : "gm_io_toggle_infamy_outliner", + "title" : "gm_options_infamy_outliner_enabled_title", + "description" : "gm_options_infamy_outliner_enabled_desc", + "callback" : "InfamyOutlinerToggleEnabled", + "value" : "Enabled", + "default_value" : true, + }, + { + "type" : "divider", + "size" : 8, + }, + + { + "type" : "toggle", + "id" : "gm_io_toggle_custom_use_hue", + "title" : "gm_options_custom_use_hue_title", + "description" : "gm_options_custom_use_hue_desc", + "callback" : "InfamyOutlinerSetUseHSV", + "value" : "UseHSV", + "default_value" : true, + }, + { + "type" : "slider", + "id" : "gm_cwl_colour_slider_rh", + "title" : "gm_options_custom_rh_title", + "description" : "gm_options_custom_rh_desc", + "callback" : "InfamyOutlinerSetRedHue", + "value" : "RH", + "default_value" : 0.6, + "min" : 0, + "max" : 1, + "step" : 0.01, + }, + { + "type" : "slider", + "id" : "gm_io_colour_slider_gs", + "title" : "gm_options_custom_gs_title", + "description" : "gm_options_custom_gs_desc", + "callback" : "InfamyOutlinerSetGreenSaturation", + "value" : "GS", + "default_value" : 1, + "min" : 0, + "max" : 1, + "step" : 0.01, + }, + { + "type" : "slider", + "id" : "gm_io_colour_slider_bv", + "title" : "gm_options_custom_bv_title", + "description" : "gm_options_custom_bv_desc", + "callback" : "InfamyOutlinerSetBlueValue", + "value" : "BV", + "default_value" : 0.2, + "min" : 0, + "max" : 1, + "step" : 0.01, + }, + { + "type" : "divider", + "size" : 64, + }, + + { + "type" : "button", + "id" : "gm_io_toggle_rainbow", + "title" : "gm_options_infamy_outliner_clear_data_title", + "description" : "gm_options_infamy_outliner_clear_data_desc", + "callback" : "InfamyOutlinerClearInfamyData", + } + + ] + +} diff --git a/GoonMod/mod.txt b/GoonMod/mod.txt index f70ea3e..a3d4485 100644 --- a/GoonMod/mod.txt +++ b/GoonMod/mod.txt @@ -69,6 +69,9 @@ { "hook_id" : "lib/managers/challengemanager", "script_path" : "goonbase.lua" }, { "hook_id" : "lib/managers/menu/playerprofileguiobject", "script_path" : "goonbase.lua" }, { "hook_id" : "lib/managers/menu/walletguiobject", "script_path" : "goonbase.lua" }, - { "hook_id" : "lib/managers/experiencemanager", "script_path" : "goonbase.lua" } + { "hook_id" : "lib/managers/experiencemanager", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/managers/skilltreemanager", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/managers/menu/skilltreegui", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/network/networkgame", "script_path" : "goonbase.lua" } ] } diff --git a/GoonMod/mods/infamy_outliner.lua b/GoonMod/mods/infamy_outliner.lua new file mode 100644 index 0000000..05318df --- /dev/null +++ b/GoonMod/mods/infamy_outliner.lua @@ -0,0 +1,294 @@ + +-- Mod Definition +local Mod = class( BaseMod ) +Mod.id = "InfamyOutliner" +Mod.Name = "Infamy Outliner" +Mod.Desc = "Provides an outline of your skill trees prior to going infamous for you to rebuild on." +Mod.Requirements = {} +Mod.Incompatibilities = {} +Mod.EnabledByDefault = true + +Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod:ID(), function() + GoonBase.Mods:RegisterMod( Mod ) +end) + +if not Mod:IsEnabled() then + return +end + +-- Infamy Outliner +_G.GoonBase.InfamyOutliner = _G.GoonBase.InfamyOutliner or {} +local InfamyOutliner = _G.GoonBase.InfamyOutliner +InfamyOutliner.SaveFile = "goonmod_infamy_outline.txt" +InfamyOutliner.MenuFile = "infamy_outliner_menu.txt" +InfamyOutliner.Outline = {} + +-- Options +GoonBase.Options.InfamyOutliner = GoonBase.Options.InfamyOutliner or {} +GoonBase.Options.InfamyOutliner.Enabled = GoonBase.Options.InfamyOutliner.Enabled or true +GoonBase.Options.InfamyOutliner.RH = GoonBase.Options.InfamyOutliner.RH or 0.8 +GoonBase.Options.InfamyOutliner.GS = GoonBase.Options.InfamyOutliner.GS or 1.0 +GoonBase.Options.InfamyOutliner.BV = GoonBase.Options.InfamyOutliner.BV or 0.4 +GoonBase.Options.InfamyOutliner.UseHSV = GoonBase.Options.InfamyOutliner.UseHSV or true + +-- Color +InfamyOutliner.Color = ColorHSVRGB:new( GoonBase.Options.InfamyOutliner, Color.purple:with_alpha(0) ) + +function InfamyOutliner:IsEnabled() + return GoonBase.Options.InfamyOutliner.Enabled or false +end + +function InfamyOutliner:LoadInfamySkillData() + + local path = SavePath .. InfamyOutliner.SaveFile + local file = io.open( path, "r" ) + if file then + local data = file:read("*all") + file:close() + if not string.is_nil_or_empty( data ) then + InfamyOutliner.Outline = json.decode( data ) + end + end + +end + +function InfamyOutliner:SaveInfamySkillData() + + local tree_data = managers.skilltree._global.skill_switches + InfamyOutliner.Outline = tree_data + tree_data = json.encode( InfamyOutliner.Outline ) + + local path = SavePath .. InfamyOutliner.SaveFile + local file = io.open( path, "w+" ) + if file then + file:write( tostring(tree_data) ) + file:close() + else + Print("[Error] Could not save infamy skill tree data!") + end + +end + +function InfamyOutliner:ClearInfamySkillData() + + local path = SavePath .. InfamyOutliner.SaveFile + local success, remove_error = os.remove( path ) + if success then + InfamyOutliner.Outline = {} + else + Print("Could not clear infamy outliner skill data: " .. tostring(remove_error)) + end + +end + +function InfamyOutliner:UpdateSkillItem( item, skill_id, infamy_outline, infamy_ace ) + + if not InfamyOutliner:IsEnabled() then + return + end + + if not infamy_outline then + infamy_outline = item._infamy_outline + if not infamy_outline then + return + end + end + if not infamy_ace then + infamy_ace = item._infamy_ace + if not infamy_ace then + return + end + end + + local skill_set = managers.skilltree:get_selected_skill_switch() + local tree = managers.skilltree._global.skill_switches[ skill_set ] + local outline = InfamyOutliner.Outline[ skill_set ] + + if not outline then + return + end + + local tree_unlocked = tree.skills[skill_id].unlocked + local outline_unlocked = outline.skills[skill_id].unlocked + + if self:IsSkillIDSkillTree( skill_id ) and tree_unlocked > 0 then + infamy_outline:set_alpha( 0 ) + infamy_ace:set_alpha( 0 ) + return + end + + if tree_unlocked and tree_unlocked >= outline_unlocked then + infamy_outline:set_alpha( 0 ) + infamy_ace:set_alpha( 0 ) + return + end + + if outline_unlocked then + infamy_outline:set_alpha( outline_unlocked > 0 and 1 or 0 ) + infamy_ace:set_alpha( outline_unlocked > 1 and 1 or 0 ) + end + +end + +function InfamyOutliner:IsSkillIDSkillTree( id ) + for k, v in pairs( tweak_data.skilltree.trees ) do + if v.skill == id then + return true + end + end + return false +end + +function InfamyOutliner:GetColor(alpha) + return InfamyOutliner.Color:GetColor( alpha ) +end + +function InfamyOutliner:ShowPreviewMenuItem() + + if not managers.menu_component then + return + end + + local ws = managers.menu_component._ws + self._panel = ws:panel():panel() + + local w, h = self._panel:w() * 0.35, 48 + self._color_rect = self._panel:rect({ + w = w, + h = h, + color = Color.red, + blend_mode = "add", + layer = tweak_data.gui.MOUSE_LAYER - 50, + }) + self._color_rect:set_right( self._panel:right() ) + self._color_rect:set_top( self._panel:h() * 0.265 ) + + self:UpdatePreview() + +end + +function InfamyOutliner:DestroyPreviewMenuItem() + + if alive(self._panel) then + + self._panel:remove( self._color_rect ) + self._panel:remove( self._panel ) + + self._color_rect = nil + self._panel = nil + + end + +end + +function InfamyOutliner:UpdatePreview( t ) + if not alive(self._panel) or not alive(self._color_rect) or not InfamyOutliner.Color then + return + end + self._color_rect:set_color( InfamyOutliner.Color:GetColor() ) +end + +-- Menu +Hooks:Add("MenuManagerInitialize", "MenuManagerInitialize_" .. Mod:ID(), function( menu_manager ) + + InfamyOutliner:LoadInfamySkillData() + + InfamyOutliner._increase_infamous_orig = MenuCallbackHandler._increase_infamous + MenuCallbackHandler._increase_infamous = function(self) + InfamyOutliner:SaveInfamySkillData() + InfamyOutliner._increase_infamous_orig(self) + end + + -- Callbacks + MenuCallbackHandler.InfamyOutlinerChangedFocus = function( node, focus ) + if focus then + InfamyOutliner:ShowPreviewMenuItem() + else + InfamyOutliner:DestroyPreviewMenuItem() + end + end + + MenuCallbackHandler.InfamyOutlinerToggleEnabled = function( this, item ) + GoonBase.Options.InfamyOutliner.Enabled = item:value() == "on" and true or false + InfamyOutliner:UpdatePreview() + end + + MenuCallbackHandler.InfamyOutlinerSetUseHSV = function( this, item ) + GoonBase.Options.InfamyOutliner.UseHSV = item:value() == "on" and true or false + InfamyOutliner:UpdatePreview() + end + + MenuCallbackHandler.InfamyOutlinerSetRedHue = function( this, item ) + GoonBase.Options.InfamyOutliner.RH = tonumber( item:value() ) + InfamyOutliner:UpdatePreview() + end + + MenuCallbackHandler.InfamyOutlinerSetGreenSaturation = function( this, item ) + GoonBase.Options.InfamyOutliner.GS = tonumber( item:value() ) + InfamyOutliner:UpdatePreview() + end + + MenuCallbackHandler.InfamyOutlinerSetBlueValue = function( this, item ) + GoonBase.Options.InfamyOutliner.BV = tonumber( item:value() ) + InfamyOutliner:UpdatePreview() + end + + MenuCallbackHandler.InfamyOutlinerClearInfamyData = function( this, item ) + InfamyOutliner:ClearInfamySkillData() + end + + MenuHelper:LoadFromJsonFile( GoonBase.MenusPath .. InfamyOutliner.MenuFile, GoonBase.InfamyOutliner, GoonBase.Options.InfamyOutliner ) + +end) + +Hooks:Add("SkillTreeSkillItemPostInit", "SkillTreeSkillItemPostInit_" .. Mod:ID(), function(gui, skill_id, tier_panel, num_skills, i, tree, tier, w, h, skill_refresh_skills) + + if not InfamyOutliner:IsEnabled() then + return + end + + local state_image = gui._skill_panel:child("state_image") + local infamy_outline = gui._skill_panel:bitmap({ + name = "infamy_outline", + texture = "guis/textures/pd2/hot_cold_glow", + alpha = 0, + color = InfamyOutliner.Color:GetColor(), + layer = -1 + }) + infamy_outline:set_size(state_image:w(), state_image:h()) + infamy_outline:set_blend_mode("add") + infamy_outline:set_rotation(360) + infamy_outline:set_center(state_image:center()) + gui._infamy_outline = infamy_outline + + local infamy_ace = gui._skill_panel:bitmap({ + name = "infamy_outline_ace", + texture = "guis/textures/pd2/skilltree/ace", + alpha = 0, + color = InfamyOutliner.Color:GetColor(), + layer = -1 + }) + infamy_ace:set_size(state_image:w() * 2, state_image:h() * 2) + infamy_ace:set_blend_mode("add") + infamy_ace:set_rotation(360) + infamy_ace:set_center(state_image:center()) + gui._infamy_ace = infamy_ace + + if not managers.skilltree or not InfamyOutliner.Outline then + return + end + + InfamyOutliner:UpdateSkillItem( gui, skill_id, infamy_outline, infamy_ace ) + +end) + +Hooks:Add("SkillTreeSkillItemPostRefresh", "SkillTreeSkillItemPostRefresh_" .. Mod:ID(), function(gui, locked) + + if not InfamyOutliner:IsEnabled() then + return + end + + local skill_id = gui._skill_panel:name() + InfamyOutliner:UpdateSkillItem( gui, skill_id ) + +end) diff --git a/GoonMod/mods/zoom_sensitivity.lua b/GoonMod/mods/zoom_sensitivity.lua index f289a5f..e86e559 100644 --- a/GoonMod/mods/zoom_sensitivity.lua +++ b/GoonMod/mods/zoom_sensitivity.lua @@ -34,7 +34,7 @@ Hooks:Add("MenuManagerSetupGoonBaseMenu", "MenuManagerSetupGoonBaseMenu_" .. Mod callback = "toggle_zoom_sensitivity", value = GoonBase.Options.IronsightSensitivity.Enabled, menu_id = "goonbase_options_menu", - priority = 101, + priority = 1, }) end) diff --git a/GoonMod/mods/custom_colours/color_hsvrgb.lua b/GoonMod/req/color_hsvrgb.lua similarity index 100% rename from GoonMod/mods/custom_colours/color_hsvrgb.lua rename to GoonMod/req/color_hsvrgb.lua From 115e4fdfe73d7bbed7c7cc1509114d4e9fa5a71d Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Mon, 23 Mar 2015 13:52:26 +1100 Subject: [PATCH 77/92] Cleaned up custom laser/flashlight mods --- GoonMod/loc/en.txt | 25 ++++++------------- ...ights.lua => flashlight_player_weapon.lua} | 3 +-- ...pon_lasers.lua => laser_player_weapon.lua} | 20 +-------------- .../{world_lasers.lua => laser_world.lua} | 20 +-------------- 4 files changed, 11 insertions(+), 57 deletions(-) rename GoonMod/mods/custom_colours/{weapon_flashlights.lua => flashlight_player_weapon.lua} (98%) rename GoonMod/mods/custom_colours/{player_weapon_lasers.lua => laser_player_weapon.lua} (95%) rename GoonMod/mods/custom_colours/{world_lasers.lua => laser_world.lua} (92%) diff --git a/GoonMod/loc/en.txt b/GoonMod/loc/en.txt index 9fba832..186ddde 100644 --- a/GoonMod/loc/en.txt +++ b/GoonMod/loc/en.txt @@ -255,12 +255,12 @@ "gm_options_custom_rainbow_speed_title" : "Rainbow Effect Speed", "gm_options_custom_rainbow_speed_desc" : "Sets the speed of the rainbow effect", - "gm_options_cfc_menu_title" : "Weapon Flashlight Color", + "gm_options_cfc_menu_title" : "Custom Flashlight Color - Players", "gm_options_cfc_menu_desc" : "Modify the color of weapon flashlights", "gm_options_cfc_enabled_title" : "Enable Custom Weapon Flashlight Color", "gm_options_cfc_enabled_desc" : "Use the custom set color for Weapon Flashlights", - "gm_options_cpwl_menu_title" : "Weapon Laser Colour", + "gm_options_cpwl_menu_title" : "Custom Laser Colour - Players", "gm_options_cpwl_menu_desc" : "Modify the color of your own, and your teammates weapon lasers", "gm_options_cpwl_enabled_title" : "Enable Custom Weapon Laser Colour", "gm_options_cpwl_enabled_desc" : "Use the custom set color for lasers on your own weapons", @@ -271,25 +271,16 @@ "gm_options_cpwl_teammate_lasers_network" : "Networked Colour", "gm_options_cpwl_teammate_lasers_unique" : "Unique per Person", - "gm_options_cwl_menu_title" : "World Laser Colour", + "gm_options_elc_menu_title" : "Custom Laser Colour - Enemies", + "gm_options_elc_menu_desc" : "Modify the color of lasers on weapons that enemies carry", + "gm_options_elc_enabled_title" : "Enable Custom Enemy Weapon Laser Colour", + "gm_options_elc_enabled_desc" : "Use the custom set color for lasers on enemy weapons", + + "gm_options_cwl_menu_title" : "Custom Laser Colour - World", "gm_options_cwl_menu_desc" : "Modify the color of lasers that appear in the world like on mines and sensors", "gm_options_cwl_enabled_title" : "Enable Custom World Laser Colour", "gm_options_cwl_enabled_desc" : "Use the custom set color for lasers in the world", - "Options_WeaponLaserName" : "Weapon Laser Color", - "Options_WeaponLaserDesc" : "Modify the color of weapon lasers", - "Options_WeaponLaserEnableTitle" : "Enable Custom Weapon Laser Color", - "Options_WeaponLaserEnableDesc" : "Use the custom set color for Weapon Lasers", - "Options_WeaponLaserRainbowTitle" : "Enable Rainbow Laser", - "Options_WeaponLaserRainbowDesc" : "Enable rainbow instead of the set Hue", - "Options_WeaponLaserRainbowSpeedTitle" : "Rainbow Speed", - "Options_WeaponLaserRainbowSpeedDesc" : "Set the speed of the rainbow effect", - "Options_TeammateLaserOption" : "Teammate Lasers", - "Options_TeammateLaserOptionDesc" : "Set how teammate lasers should appear", - "Options_TeammateLaser_Same" : "Use My Colour", - "Options_TeammateLaser_Theirs" : "Use Their Colour", - "Options_TeammateLaser_Unique" : "Unique per Person", - "gm_options_choose_any_character_title" : "Enable Multiple Characters", "gm_options_choose_any_character_desc" : "Allow players to join as any character they want, even with multiple of the same character", diff --git a/GoonMod/mods/custom_colours/weapon_flashlights.lua b/GoonMod/mods/custom_colours/flashlight_player_weapon.lua similarity index 98% rename from GoonMod/mods/custom_colours/weapon_flashlights.lua rename to GoonMod/mods/custom_colours/flashlight_player_weapon.lua index 0fde6c0..b910c17 100644 --- a/GoonMod/mods/custom_colours/weapon_flashlights.lua +++ b/GoonMod/mods/custom_colours/flashlight_player_weapon.lua @@ -2,11 +2,10 @@ -- Mod Definition local Mod = class( BaseMod ) Mod.id = "CustomWeaponFlashlight" -Mod.Name = "Custom Weapon Flashlight Colour" +Mod.Name = "Custom Flashlight Colour - Players" Mod.Desc = "Modify the color of flashlights attached to your weapons" Mod.Requirements = {} Mod.Incompatibilities = {} -Mod.Priority = 1 Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() GoonBase.Mods:RegisterMod( Mod ) diff --git a/GoonMod/mods/custom_colours/player_weapon_lasers.lua b/GoonMod/mods/custom_colours/laser_player_weapon.lua similarity index 95% rename from GoonMod/mods/custom_colours/player_weapon_lasers.lua rename to GoonMod/mods/custom_colours/laser_player_weapon.lua index a6cdf7a..97db43b 100644 --- a/GoonMod/mods/custom_colours/player_weapon_lasers.lua +++ b/GoonMod/mods/custom_colours/laser_player_weapon.lua @@ -2,11 +2,10 @@ -- Mod Definition local Mod = class( BaseMod ) Mod.id = "CustomWeaponLaserColour" -Mod.Name = "Custom Weapon Laser Colour" +Mod.Name = "Custom Laser Colour - Players" Mod.Desc = "Modify the color of your own, and your teammates weapon lasers" Mod.Requirements = {} Mod.Incompatibilities = {} -Mod.Priority = 1 Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() GoonBase.Mods:RegisterMod( Mod ) @@ -50,23 +49,6 @@ function Lasers:IsEnabled() return GoonBase.Options.WeaponLasers.Enabled end -function Lasers:IsRainbow() - return GoonBase.Options.UseRainbow.Rainbow -end - -function Lasers:GetColor(alpha) - if Lasers.Color == nil then - Lasers.Color = ColorHSVRGB:new() - Lasers.Color:SetOptionsTable( "WeaponLasers" ) - end - return Lasers.Color:GetColor( alpha ) or Color( alpha or 1, 1, 0, 0 ) -end - - -function Lasers:IsEnabled() - return GoonBase.Options.WeaponLasers.Enabled -end - function Lasers:IsRainbow() return GoonBase.Options.WeaponLasers.UseRainbow end diff --git a/GoonMod/mods/custom_colours/world_lasers.lua b/GoonMod/mods/custom_colours/laser_world.lua similarity index 92% rename from GoonMod/mods/custom_colours/world_lasers.lua rename to GoonMod/mods/custom_colours/laser_world.lua index ede551c..1705d21 100644 --- a/GoonMod/mods/custom_colours/world_lasers.lua +++ b/GoonMod/mods/custom_colours/laser_world.lua @@ -2,11 +2,10 @@ -- Mod Definition local Mod = class( BaseMod ) Mod.id = "CustomWorldLaserColour" -Mod.Name = "Custom World Laser Colour" +Mod.Name = "Custom Laser Colour - World" Mod.Desc = "Modify the colour of lasers that appear in the game world" Mod.Requirements = {} Mod.Incompatibilities = {} -Mod.Priority = 1 Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() GoonBase.Mods:RegisterMod( Mod ) @@ -40,23 +39,6 @@ function Lasers:IsEnabled() return GoonBase.Options.WorldLasers.Enabled end -function Lasers:IsRainbow() - return GoonBase.Options.UseRainbow.Rainbow -end - -function Lasers:GetColor(alpha) - if Lasers.Color == nil then - Lasers.Color = ColorHSVRGB:new() - Lasers.Color:SetOptionsTable( "WorldLasers" ) - end - return Lasers.Color:GetColor( alpha ) or Color( alpha or 1, 1, 0, 0 ) -end - - -function Lasers:IsEnabled() - return GoonBase.Options.WorldLasers.Enabled -end - function Lasers:IsRainbow() return GoonBase.Options.WorldLasers.UseRainbow end From 41f642b8070744331b75831a48e7616a87af5105 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Mon, 23 Mar 2015 13:53:24 +1100 Subject: [PATCH 78/92] Added Custom Enemy Weapon Laser mod --- .gitignore | 2 + GoonMod/menus/custom_enemy_laser_menu.txt | 99 +++++++ .../custom_colours/laser_enemy_weapon.lua | 264 ++++++++++++++++++ .../disabled/colors/enemy_weapon_laser.lua | 236 ---------------- 4 files changed, 365 insertions(+), 236 deletions(-) create mode 100644 GoonMod/menus/custom_enemy_laser_menu.txt create mode 100644 GoonMod/mods/custom_colours/laser_enemy_weapon.lua delete mode 100644 GoonMod/mods/disabled/colors/enemy_weapon_laser.lua diff --git a/.gitignore b/.gitignore index 7699948..ee0362c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ GoonMod/mods/akimbo_autoaim.lua GoonMod/mods/riot_shields.lua GoonMod/mods/perkdeck_jacket.lua +GoonMod/mods/choose_any_character.lua +GoonMod/loc/pd_jacket_en.txt diff --git a/GoonMod/menus/custom_enemy_laser_menu.txt b/GoonMod/menus/custom_enemy_laser_menu.txt new file mode 100644 index 0000000..fa3eaef --- /dev/null +++ b/GoonMod/menus/custom_enemy_laser_menu.txt @@ -0,0 +1,99 @@ +{ + "menu_id" : "gm_options_elc_menu", + "parent_menu_id" : "goonbase_options_menu", + "title" : "gm_options_elc_menu_title", + "description" : "gm_options_elc_menu_desc", + "focus_changed_callback" : "CustomEnemyLaserMenuChangeFocus", + "back_callback" : "ClosedGoonModOptions", + "area_bg" : "half", + "items" : [ + + { + "type" : "toggle", + "id" : "gm_elc_toggle_custom_flashlight", + "title" : "gm_options_elc_enabled_title", + "description" : "gm_options_elc_enabled_desc", + "callback" : "ToggleEnableCustomEnemyLaser", + "value" : "Enabled", + "default_value" : true, + }, + { + "type" : "divider", + "size" : 8, + }, + + { + "type" : "toggle", + "id" : "gm_elc_toggle_custom_flashlight_use_hue", + "title" : "gm_options_custom_use_hue_title", + "description" : "gm_options_custom_use_hue_desc", + "callback" : "CustomEnemyLaserToggleUseHue", + "value" : "UseHSV", + "default_value" : false, + }, + { + "type" : "slider", + "id" : "gm_elc_colour_slider_rh", + "title" : "gm_options_custom_rh_title", + "description" : "gm_options_custom_rh_desc", + "callback" : "CustomEnemyLaserSetRedHue", + "value" : "RH", + "default_value" : 0.8, + "min" : 0, + "max" : 1, + "step" : 0.01, + }, + { + "type" : "slider", + "id" : "gm_elc_colour_slider_gs", + "title" : "gm_options_custom_gs_title", + "description" : "gm_options_custom_gs_desc", + "callback" : "CustomEnemyLaserSetGreenSaturation", + "value" : "GS", + "default_value" : 0, + "min" : 0, + "max" : 1, + "step" : 0.01, + }, + { + "type" : "slider", + "id" : "gm_elc_colour_slider_bv", + "title" : "gm_options_custom_bv_title", + "description" : "gm_options_custom_bv_desc", + "callback" : "CustomEnemyLaserSetBlueValue", + "value" : "BV", + "default_value" : 0, + "min" : 0, + "max" : 1, + "step" : 0.01, + }, + { + "type" : "divider", + "size" : 64, + }, + + { + "type" : "toggle", + "id" : "gm_elc_toggle_rainbow", + "title" : "gm_options_custom_rainbow_title", + "description" : "gm_options_custom_rainbow_desc", + "callback" : "CustomEnemyLaserSetUseRainbow", + "value" : "UseRainbow", + "default_value" : false, + }, + { + "type" : "slider", + "id" : "gm_elc_slider_rainbow_speed", + "title" : "gm_options_custom_rainbow_speed_title", + "description" : "gm_options_custom_rainbow_speed_desc", + "callback" : "CustomEnemyLaserSetRainbowSpeed", + "value" : "RainbowSpeed", + "default_value" : 1, + "min" : 1, + "max" : 100, + "step" : 1, + } + + ] + +} diff --git a/GoonMod/mods/custom_colours/laser_enemy_weapon.lua b/GoonMod/mods/custom_colours/laser_enemy_weapon.lua new file mode 100644 index 0000000..15ad9b0 --- /dev/null +++ b/GoonMod/mods/custom_colours/laser_enemy_weapon.lua @@ -0,0 +1,264 @@ + +-- Mod Definition +local Mod = class( BaseMod ) +Mod.id = "CustomEnemyWeaponLaser" +Mod.Name = "Custom Laser Colour - Enemy" +Mod.Desc = "Set a custom colour for lasers attached to enemy weapons" +Mod.Requirements = {} +Mod.Incompatibilities = {} + +Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() + GoonBase.Mods:RegisterMod( Mod ) +end) + +if not Mod:IsEnabled() then + return +end + +-- Lasers +GoonBase.EnemyLasers = GoonBase.EnemyLasers or {} +local Lasers = GoonBase.EnemyLasers +Lasers.MenuFile = "custom_enemy_laser_menu.txt" +Lasers._WorldOpacity = 0.4 + +-- Options +GoonBase.Options.EnemyLasers = GoonBase.Options.EnemyLasers or {} +GoonBase.Options.EnemyLasers.Enabled = GoonBase.Options.EnemyLasers.Enabled or true +GoonBase.Options.EnemyLasers.RH = GoonBase.Options.EnemyLasers.RH or 0.8 +GoonBase.Options.EnemyLasers.GS = GoonBase.Options.EnemyLasers.GS or 0.0 +GoonBase.Options.EnemyLasers.BV = GoonBase.Options.EnemyLasers.BV or 0.0 +GoonBase.Options.EnemyLasers.UseHSV = GoonBase.Options.EnemyLasers.UseHSV or false +GoonBase.Options.EnemyLasers.UseRainbow = GoonBase.Options.EnemyLasers.UseRainbow or false +GoonBase.Options.EnemyLasers.RainbowSpeed = GoonBase.Options.EnemyLasers.RainbowSpeed or 1 + +-- Laser Colour +Lasers.Color = ColorHSVRGB:new( GoonBase.Options.EnemyLasers, Color.red:with_alpha(0.6) ) + +-- Functions +function Lasers:IsEnabled() + return GoonBase.Options.EnemyLasers.Enabled +end + +function Lasers:IsRainbow() + return GoonBase.Options.EnemyLasers.UseRainbow +end + +function Lasers:RainbowSpeed() + return GoonBase.Options.EnemyLasers.RainbowSpeed +end + +function Lasers:GetColor(alpha) + return Lasers.Color:GetColor( alpha ) +end + +function Lasers:IsNPCPlayerUnitLaser( laser ) + + if not self._laser_units_lookup then + self._laser_units_lookup = {} + end + + local laser_key = nil + if laser._unit then + laser_key = laser._unit:key() + end + if laser_key and self._laser_units_lookup[laser_key] ~= nil then + return self._laser_units_lookup[laser_key] + end + + local criminals_manager = managers.criminals + if not criminals_manager then + return + end + + for id, data in pairs(criminals_manager._characters) do + if alive(data.unit) and data.name ~= criminals_manager:local_character_name() and data.unit:inventory() and data.unit:inventory():equipped_unit() then + + local wep_base = data.unit:inventory():equipped_unit():base() + local weapon_base = data.unit:inventory():equipped_unit():base() + if Lasers:CheckWeaponForLasers( weapon_base, laser_key ) then + self._laser_units_lookup[laser_key] = true + return + end + + if weapon_base._second_gun then + if Lasers:CheckWeaponForLasers( weapon_base._second_gun:base(), laser_key ) then + self._laser_units_lookup[laser_key] = true + return + end + end + + end + end + + if laser_key then + self._laser_units_lookup[laser_key] = false + end + return false + +end + +function Lasers:CheckWeaponForLasers( weapon_base, key ) + + if weapon_base and weapon_base._factory_id and weapon_base._blueprint then + + local gadgets = managers.weapon_factory:get_parts_from_weapon_by_type_or_perk("gadget", weapon_base._factory_id, weapon_base._blueprint) + if gadgets then + for _, i in ipairs(gadgets) do + + local gadget_key = weapon_base._parts[i].unit:key() + if gadget_key == key then + return true + end + + end + end + + end + return false + +end + +function Lasers:ShowPreviewMenuItem() + + if not managers.menu_component then + return + end + + local ws = managers.menu_component._ws + self._panel = ws:panel():panel() + + local w, h = self._panel:w() * 0.35, 48 + self._color_rect = self._panel:rect({ + w = w, + h = h, + color = Color.red, + blend_mode = "add", + layer = tweak_data.gui.MOUSE_LAYER - 50, + }) + self._color_rect:set_right( self._panel:right() ) + self._color_rect:set_top( self._panel:h() * 0.265 ) + + self:UpdatePreview() + +end + +function Lasers:DestroyPreviewMenuItem() + + if alive(self._panel) then + + self._panel:remove( self._color_rect ) + self._panel:remove( self._panel ) + + self._color_rect = nil + self._panel = nil + + end + +end + +function Lasers:UpdatePreview( t ) + + if not alive(self._panel) or not alive(self._color_rect) or not Lasers.Color then + return + end + + if self:IsRainbow() and t then + self._color_rect:set_color( Lasers.Color:GetRainbowColor(t, self:RainbowSpeed()) ) + else + self._color_rect:set_color( Lasers.Color:GetColor() ) + end + +end + +-- Menu +Hooks:Add("MenuManagerInitialize", "MenuManagerInitialize_" .. Mod:ID(), function( menu_manager ) + + -- Callbacks + MenuCallbackHandler.CustomEnemyLaserMenuChangeFocus = function( node, focus ) + if focus then + Lasers:ShowPreviewMenuItem() + else + Lasers:DestroyPreviewMenuItem() + end + end + + MenuCallbackHandler.ToggleEnableCustomEnemyLaser = function( this, item ) + GoonBase.Options.EnemyLasers.Enabled = item:value() == "on" and true or false + Lasers:UpdatePreview() + end + + MenuCallbackHandler.CustomEnemyLaserToggleUseHue = function( this, item ) + GoonBase.Options.EnemyLasers.UseHSV = item:value() == "on" and true or false + Lasers:UpdatePreview() + end + + MenuCallbackHandler.CustomEnemyLaserSetRedHue = function( this, item ) + GoonBase.Options.EnemyLasers.RH = tonumber( item:value() ) + Lasers:UpdatePreview() + end + + MenuCallbackHandler.CustomEnemyLaserSetGreenSaturation = function( this, item ) + GoonBase.Options.EnemyLasers.GS = tonumber( item:value() ) + Lasers:UpdatePreview() + end + + MenuCallbackHandler.CustomEnemyLaserSetBlueValue = function( this, item ) + GoonBase.Options.EnemyLasers.BV = tonumber( item:value() ) + Lasers:UpdatePreview() + end + + MenuCallbackHandler.CustomEnemyLaserSetUseRainbow = function( this, item ) + GoonBase.Options.EnemyLasers.UseRainbow = item:value() == "on" and true or false + Lasers:UpdatePreview() + end + + MenuCallbackHandler.CustomEnemyLaserSetRainbowSpeed = function( this, item ) + GoonBase.Options.EnemyLasers.RainbowSpeed = tonumber( item:value() ) + end + + MenuHelper:LoadFromJsonFile( GoonBase.MenusPath .. Lasers.MenuFile, GoonBase.EnemyLasers, GoonBase.Options.EnemyLasers ) + +end) + +-- Hooks +Hooks:Add("MenuUpdate", "MenuUpdate_" .. Mod:ID(), function(t, dt) + if Lasers:IsRainbow() then + Lasers:UpdatePreview( t ) + end +end) + +Hooks:Add("GameSetupUpdate", "GameSetupUpdate_" .. Mod:ID(), function(t, dt) + if Lasers:IsRainbow() then + Lasers:UpdatePreview( t ) + end +end) + +Hooks:Add("WeaponLaserPostSetColorByTheme", "WeaponLaserPostSetColorByTheme_CustomEnemyLaser", function(laser, unit) + + if not Lasers:IsEnabled() then + return + end + + if laser._is_npc and not Lasers:IsNPCPlayerUnitLaser( laser ) then + laser:set_color( Lasers:GetColor(Lasers._WorldOpacity) ) + end + +end) + +Hooks:Add("WeaponLaserUpdate", "WeaponLaserUpdate_EnemyRainbow", function(laser, unit, t, dt) + + if not Lasers:IsEnabled() then + return + end + + if laser._is_npc and not Lasers:IsNPCPlayerUnitLaser( laser ) then + + if not Lasers:IsRainbow() then + laser:set_color( Lasers:GetColor() ) + else + laser:set_color( Lasers.Color:GetRainbowColor( t, Lasers:RainbowSpeed() ):with_alpha(Lasers._WorldOpacity) ) + end + + end + +end) diff --git a/GoonMod/mods/disabled/colors/enemy_weapon_laser.lua b/GoonMod/mods/disabled/colors/enemy_weapon_laser.lua deleted file mode 100644 index ac5ff78..0000000 --- a/GoonMod/mods/disabled/colors/enemy_weapon_laser.lua +++ /dev/null @@ -1,236 +0,0 @@ - --- Mod Definition -local Mod = class( BaseMod ) -Mod.id = "CustomEnemyWeaponLaser" -Mod.Name = "Custom Enemy Laser Colour" -Mod.Desc = "Set a custom colour for lasers attached to enemy weapons" -Mod.Requirements = {} -Mod.Incompatibilities = {} -Mod.Priority = 1 - -Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() - GoonBase.Mods:RegisterMod( Mod ) -end) - -if not Mod:IsEnabled() then - return -end - --- Lasers -GoonBase.EnemyLaser = GoonBase.EnemyLaser or {} -local Laser = GoonBase.EnemyLaser -Laser.MenuId = "goonbase_enemy_laser_menu" -Laser.Color = nil - --- Options -if GoonBase.Options.EnemyLaser == nil then - GoonBase.Options.EnemyLaser = {} - GoonBase.Options.EnemyLaser.Enabled = false - GoonBase.Options.EnemyLaser.R = 1 - GoonBase.Options.EnemyLaser.G = 0.1 - GoonBase.Options.EnemyLaser.B = 0.1 - GoonBase.Options.EnemyLaser.HSV = false - GoonBase.Options.EnemyLaser.Rainbow = false - GoonBase.Options.EnemyLaser.RainbowSpeed = 10 -end - --- Functions -function Laser:IsEnabled() - return GoonBase.Options.EnemyLaser.Enabled -end - -function Laser:IsRainbow() - return GoonBase.Options.EnemyLaser.Rainbow -end - -function Laser:GetColor(alpha) - if Laser.Color == nil then - Laser.Color = ColorHSVRGB:new() - Laser.Color:SetOptionsTable( "EnemyLaser" ) - end - return Laser.Color:GetColor( alpha ) or Color( alpha or 1, 1, 0, 0 ) -end - -function Laser:IsNPCPlayerUnitLaser( laser ) - - if not self._laser_units_lookup then - self._laser_units_lookup = {} - end - - local laser_key = nil - if laser._unit then - laser_key = laser._unit:key() - end - if laser_key and self._laser_units_lookup[laser_key] ~= nil then - return self._laser_units_lookup[laser_key] - end - - local criminals_manager = managers.criminals - if not criminals_manager then - return - end - - for id, data in pairs(criminals_manager._characters) do - if data.unit ~= nil and alive(data.unit) and data.name ~= criminals_manager:local_character_name() then - - if data.unit:inventory() and data.unit:inventory():equipped_unit() then - - local wep_base = data.unit:inventory():equipped_unit():base() - if wep_base then - - if wep_base._factory_id ~= nil and wep_base._blueprint ~= nil then - - local gadgets = managers.weapon_factory:get_parts_from_weapon_by_type_or_perk("gadget", wep_base._factory_id, wep_base._blueprint) - if gadgets then - local gadget - for _, i in ipairs(gadgets) do - - gadget = wep_base._parts[i] - gadget = gadget.unit:base() - - if gadget == laser then - if laser_key then - self._laser_units_lookup[laser_key] = true - end - return true - end - - end - end - - end - - end - - end - - end - end - - if laser_key then - self._laser_units_lookup[laser_key] = false - end - return false - -end - --- Menu -Hooks:Add("MenuManagerSetupCustomMenus", "MenuManagerSetupCustomMenus_EnemyLaser", function(menu_manager, menu_nodes) - MenuHelper:NewMenu( Laser.MenuId ) -end) - -Hooks:Add("MenuManagerSetupGoonBaseMenu", "MenuManagerSetupGoonBaseMenu_EnemyLaser", function(menu_manager, menu_nodes) - - -- Submenu Button - MenuHelper:AddButton({ - id = "enemy_laser_button", - title = "Options_EnemyLaserName", - desc = "Options_EnemyLaserDesc", - next_node = Laser.MenuId, - menu_id = "goonbase_options_menu" - }) - - -- Enabled Toggle - MenuCallbackHandler.toggle_custom_enemy_laser_color = function(this, item) - GoonBase.Options.EnemyLaser.Enabled = item:value() == "on" and true or false - GoonBase.Options:Save() - end - - MenuHelper:AddToggle({ - id = "toggle_custom_enemy_laser_color", - title = "Options_EnemyLaserEnableTitle", - desc = "Options_EnemyLaserEnableDesc", - callback = "toggle_custom_enemy_laser_color", - value = GoonBase.Options.EnemyLaser.Enabled, - menu_id = Laser.MenuId, - priority = 11 - }) - - -- RGB/HSV Colour - Laser.Color = ColorHSVRGB:new() - Laser.Color:SetID( "enemy_laser" ) - Laser.Color:SetPriority( 5 ) - Laser.Color:SetOptionsTable( "EnemyLaser" ) - Laser.Color:SetupMenu( Laser.MenuId ) - - -- Rainbow Laser - MenuCallbackHandler.toggle_enemy_laser_rainbow = function(this, item) - GoonBase.Options.EnemyLaser.Rainbow = item:value() == "on" and true or false - GoonBase.Options:Save() - end - - MenuCallbackHandler.enemy_laser_rainbow_speed = function(this, item) - GoonBase.Options.EnemyLaser.RainbowSpeed = item:value() - GoonBase.Options:Save() - end - - MenuHelper:AddToggle({ - id = "toggle_enemy_laser_rainbow", - title = "Options_EnemyLaserRainbowTitle", - desc = "Options_EnemyLaserRainbowDesc", - callback = "toggle_enemy_laser_rainbow", - value = GoonBase.Options.EnemyLaser.Rainbow, - menu_id = Laser.MenuId, - priority = 2 - }) - - MenuHelper:AddSlider({ - id = "enemy_laser_rainbow_speed", - title = "Options_EnemyLaserRainbowSpeedTitle", - desc = "Options_EnemyLaserRainbowSpeedDesc", - callback = "enemy_laser_rainbow_speed", - value = GoonBase.Options.EnemyLaser.RainbowSpeed, - min = 1, - max = 100, - step = 1, - show_value = true, - menu_id = Laser.MenuId, - priority = 1, - }) - -end) - -Hooks:Add("MenuManagerBuildCustomMenus", "MenuManagerBuildCustomMenus_EnemyLaser", function(menu_manager, mainmenu_nodes) - local menu_id = Laser.MenuId - local data = { - area_bg = "half" - } - mainmenu_nodes[menu_id] = MenuHelper:BuildMenu( menu_id, data ) -end) - --- Hooks -Hooks:Add("WeaponLaserPostSetColorByTheme", "WeaponLaserPostSetColorByTheme_CustomEnemyLaser", function(laser, unit) - - if not Laser:IsEnabled() then - return - end - - if not laser._is_npc or Laser:IsNPCPlayerUnitLaser( laser ) then - return - end - - laser:set_color( Laser:GetColor() ) - -end) - -Hooks:Add("WeaponLaserUpdate", "WeaponLaserUpdate_EnemyRainbow", function(laser, unit, t, dt) - - if not Laser:IsEnabled() then - return - end - - if not laser._is_npc or Laser:IsNPCPlayerUnitLaser( laser ) then - return - end - - if not Laser:IsRainbow() then - laser:set_color( Laser:GetColor() ) - end - - if Laser:IsRainbow() then - Laser:GetColor() - local r, g, b = Laser.Color:ToRGB( math.sin(GoonBase.Options.EnemyLaser.RainbowSpeed * t), GoonBase.Options.EnemyLaser.G, GoonBase.Options.EnemyLaser.B ) - laser:set_color( Color(r, g, b) ) - end - -end) From 3498aebdfb0d8dcd94f2b94afb5560d25bb983bd Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Wed, 25 Mar 2015 00:34:08 +1100 Subject: [PATCH 79/92] Added Disable Inventory Notifications mods --- GoonMod/lua/BlackMarketGUI.lua | 7 ++++ GoonMod/lua/BlackMarketManager.lua | 18 +++++++++ GoonMod/lua/SystemMenuManager.lua | 9 +++++ GoonMod/mods/disable_inventory_new_item.lua | 44 +++++++++++++++++++++ 4 files changed, 78 insertions(+) create mode 100644 GoonMod/mods/disable_inventory_new_item.lua diff --git a/GoonMod/lua/BlackMarketGUI.lua b/GoonMod/lua/BlackMarketGUI.lua index b76f64a..010078b 100644 --- a/GoonMod/lua/BlackMarketGUI.lua +++ b/GoonMod/lua/BlackMarketGUI.lua @@ -1,5 +1,6 @@ CloneClass( BlackMarketGui ) +CloneClass( BlackMarketGuiSlotItem ) CloneClass( BlackMarketGuiButtonItem ) local is_win32 = SystemInfo:platform() == Idstring("WIN32") @@ -2018,3 +2019,9 @@ function BlackMarketGui._preview_weapon(self, data) self.orig._preview_weapon(self, data) Hooks:Call("BlackMarketGUIOnPreviewWeapon", self, data) end + +Hooks:RegisterHook("BlackMarketGUISlotItemOnRefresh") +function BlackMarketGuiSlotItem.refresh(self) + self.orig.refresh(self) + Hooks:Call("BlackMarketGUISlotItemOnRefresh", self) +end diff --git a/GoonMod/lua/BlackMarketManager.lua b/GoonMod/lua/BlackMarketManager.lua index 20373ea..0d299f7 100644 --- a/GoonMod/lua/BlackMarketManager.lua +++ b/GoonMod/lua/BlackMarketManager.lua @@ -39,6 +39,24 @@ function BlackMarketManager.get_inventory_category(self, category) end +Hooks:Call("BlackMarketManagerGotAnyNewDrop") +function BlackMarketManager.got_any_new_drop(self) + local r = Hooks:ReturnCall("BlackMarketManagerGotAnyNewDrop", self) + if r ~= nil then + return r + end + return self.orig.got_any_new_drop(self) +end + +Hooks:Call("BlackMarketManagerGotNewDrop") +function BlackMarketManager.got_new_drop(self, global_value, category, id) + local r = Hooks:ReturnCall("BlackMarketManagerGotNewDrop", self, global_value, category, id) + if r ~= nil then + return r + end + return self.orig.got_new_drop(self, global_value, category, id) +end + function BlackMarketManager:get_mods_on_weapon(category, slot) local _global = Global.blackmarket_manager if not _global.crafted_items[category] or not _global.crafted_items[category][slot] then diff --git a/GoonMod/lua/SystemMenuManager.lua b/GoonMod/lua/SystemMenuManager.lua index 68a0dbf..a3c4374 100644 --- a/GoonMod/lua/SystemMenuManager.lua +++ b/GoonMod/lua/SystemMenuManager.lua @@ -10,3 +10,12 @@ function GenericSystemMenuManager.init( self ) Hooks:Call("GenericSystemMenuManagerPostInit", self) self.orig.init( self ) end + +Hooks:RegisterHook("GenericSystemMenuManagerPreShowNewUnlock") +function GenericSystemMenuManager.show_new_unlock( self, data ) + local r = Hooks:ReturnCall("GenericSystemMenuManagerPreShowNewUnlock", self, data) + if r ~= nil then + return + end + self.orig.show_new_unlock( self, data ) +end diff --git a/GoonMod/mods/disable_inventory_new_item.lua b/GoonMod/mods/disable_inventory_new_item.lua new file mode 100644 index 0000000..3b06d0e --- /dev/null +++ b/GoonMod/mods/disable_inventory_new_item.lua @@ -0,0 +1,44 @@ + +-- Mod Definition +local Mod = class( BaseMod ) +Mod.id = "DisableInventoryNewItem" +Mod.Name = "Disable New Item Notifications" +Mod.Desc = "Disables the exclamation point icons, and new item unlocked notifications." +Mod.Requirements = {} +Mod.Incompatibilities = {} + +Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod:ID(), function() + GoonBase.Mods:RegisterMod( Mod ) +end) + +if not Mod:IsEnabled() then + return +end + +-- Hooks +Hooks:Add("BlackMarketGUISlotItemOnRefresh", "BlackMarketGUISlotItemOnRefresh_" .. Mod:ID(), function(self) + + if self._data.new_drop_data then + local newdrop = self._data.new_drop_data + if newdrop[1] and newdrop[2] and newdrop[3] then + managers.blackmarket:remove_new_drop(newdrop[1], newdrop[2], newdrop[3]) + if newdrop.icon then + newdrop.icon:parent():remove(newdrop.icon) + end + self._data.new_drop_data = nil + end + end + +end) + +Hooks:Add("BlackMarketManagerGotAnyNewDrop", "BlackMarketManagerGotAnyNewDrop_" .. Mod:ID(), function(self) + return false +end) + +Hooks:Add("BlackMarketManagerGotNewDrop", "BlackMarketManagerGotNewDrop_" .. Mod:ID(), function(self, global_value, category, id) + return false +end) + +Hooks:Add("GenericSystemMenuManagerPreShowNewUnlock", "GenericSystemMenuManagerPreShowNewUnlock_" .. Mod:ID(), function(self, data) + return false +end) From 3f793b351fb756b4bc94760ece5f58b1172ae063 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Sat, 28 Mar 2015 23:52:52 +1100 Subject: [PATCH 80/92] Added bypass auto-disable on update to options menu --- GoonMod/goonbase.lua | 4 +-- GoonMod/loc/en.txt | 3 ++ GoonMod/req/autils.lua | 19 ----------- GoonMod/req/updates.lua | 74 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 79 insertions(+), 21 deletions(-) create mode 100644 GoonMod/req/updates.lua diff --git a/GoonMod/goonbase.lua b/GoonMod/goonbase.lua index dcd623d..7c42abc 100644 --- a/GoonMod/goonbase.lua +++ b/GoonMod/goonbase.lua @@ -4,7 +4,7 @@ if not _G.GoonBase then _G.GoonBase = {} GoonBase.Version = 100 - GoonBase.GameVersion = "1.29.0" + GoonBase.GameVersion = "1.30.0" GoonBase.SupportedVersion = true GoonBase.Path = "" @@ -169,7 +169,7 @@ if not GoonBase.HasLoadedScripts then SafeDoFile( GoonBase.RequiresFolder .. v ) end - GoonBase.SupportedVersion = GoonBase.Utils:GameUpdateVersionCheck() + GoonBase.SupportedVersion = GoonBase.Updates:GameUpdateVersionCheck() -- Run hooks if GoonBase.SupportedVersion and Hooks ~= nil then diff --git a/GoonMod/loc/en.txt b/GoonMod/loc/en.txt index 186ddde..814e08e 100644 --- a/GoonMod/loc/en.txt +++ b/GoonMod/loc/en.txt @@ -17,6 +17,9 @@ "gm_mods_menu_disabled" : "All Modifications Disabled", "gm_mods_menu_disabled_desc" : "All GoonMod modifications have been disabled due to a game update. Either wait for a game update, or bypass the update-lock from the options menu at your own risk.", + "gm_mods_version_bypass_title" : "Force Load Mods", + "gm_mods_version_bypass_desc" : "Bypass the version check that prevents mods from loading after an update, and force mods to load.\nThis may cause errors and crashes, use this at your own risk!", + "gm_options_corpse_menu_title" : "Corpses", "gm_options_corpse_menu_desc" : "Change settings for the ingame corpses", "gm_options_corpse_custom_title" : "Use Custom Corpse Amount", diff --git a/GoonMod/req/autils.lua b/GoonMod/req/autils.lua index 8424380..228e539 100644 --- a/GoonMod/req/autils.lua +++ b/GoonMod/req/autils.lua @@ -1,25 +1,6 @@ _G.GoonBase.Utils = _G.GoonBase.Utils or {} -function GoonBase.Utils:GameUpdateVersionCheck() - - local mod_version = GoonBase.GameVersion:split("[.]") - local game_version = Application:version():split("[.]") - - if not mod_version or not game_version then - return false - end - - for i = 1, 2, 1 do - if mod_version[i] < game_version[i] then - return false - end - end - - return true - -end - -- Custom "Base64" Implementation _G.GoonBase.Utils.Base64 = _G.GoonBase.Utils.Base64 or {} local Base64 = _G.GoonBase.Utils.Base64 diff --git a/GoonMod/req/updates.lua b/GoonMod/req/updates.lua new file mode 100644 index 0000000..dc8c7cc --- /dev/null +++ b/GoonMod/req/updates.lua @@ -0,0 +1,74 @@ + +_G.GoonBase.Updates = _G.GoonBase.Updates or {} +local Updates = GoonBase.Updates +Updates.MenuPriority = 500 + +GoonBase.Options.Updates = GoonBase.Options.Updates or {} +GoonBase.Options.Updates.LastCheckedVersion = GoonBase.Options.Updates.LastCheckedVersion or nil +GoonBase.Options.Updates.BypassVersion = GoonBase.Options.Updates.BypassVersion or false + +function Updates:GameUpdateVersionCheck() + + if Updates:HasGameUpdated() then + GoonBase.Options.Updates.BypassVersion = false + end + GoonBase.Options.Updates.LastCheckedVersion = Application:version() + + if Updates:IsBypassingVersionCheck() then + return true + else + + local mod_version = GoonBase.GameVersion:split("[.]") + local game_version = Application:version():split("[.]") + + if not mod_version or not game_version then + return false + end + + for i = 1, 2, 1 do + if mod_version[i] < game_version[i] then + return false + end + end + + return true + + end + +end + +function Updates:HasGameUpdated() + return Application:version() ~= GoonBase.Options.Updates.LastCheckedVersion +end + +function Updates:IsBypassingVersionCheck() + return GoonBase.Options.Updates.BypassVersion +end + +Hooks:Add("MenuManagerSetupGoonBaseMenu", "MenuManagerSetupGoonBaseMenu_GoonModUpdates", function(menu_manager, menu_nodes) + + -- Menu + MenuCallbackHandler.goonmod_bypass_version_check = function(this, item) + GoonBase.Options.Updates.BypassVersion = Utils:ToggleItemToBoolean(item) + GoonBase.Options:Save() + end + + MenuHelper:AddToggle({ + id = "gm_updates_bypass_version", + title = "gm_mods_version_bypass_title", + desc = "gm_mods_version_bypass_desc", + callback = "goonmod_bypass_version_check", + menu_id = "goonbase_options_menu", + value = GoonBase.Options.Updates.BypassVersion, + default_value = false, + priority = Updates.MenuPriority + 2, + }) + + MenuHelper:AddDivider({ + size = 16, + menu_id = "goonbase_options_menu", + priority = Updates.MenuPriority + 1 + }) + +end) + From 7f4fb375f24d44aabea57a14a887803bef094d7f Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Sat, 28 Mar 2015 23:53:17 +1100 Subject: [PATCH 81/92] Merged goontest's Blackmarket Grenade tab change from legacy branch --- GoonMod/lua/BlackMarketGUI.lua | 117 +++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) diff --git a/GoonMod/lua/BlackMarketGUI.lua b/GoonMod/lua/BlackMarketGUI.lua index 010078b..fabcd70 100644 --- a/GoonMod/lua/BlackMarketGUI.lua +++ b/GoonMod/lua/BlackMarketGUI.lua @@ -785,6 +785,115 @@ function BlackMarketGui.populate_melee_weapons(self, data) end +Hooks:RegisterHook("BlackMarketGUIOnPopulateGrenades") +Hooks:RegisterHook("BlackMarketGUIOnPopulateGrenadesActionList") +function BlackMarketGui.populate_grenades(self, data) + + Hooks:Call("BlackMarketGUIOnPopulateGrenades", self, data) + + local new_data = {} + local sort_data = {} + local xd, yd, x_td, y_td, x_sn, y_sn, x_gv, y_gv + local m_tweak_data = tweak_data.blackmarket.grenades + local l_tweak_data = tweak_data.lootdrop.global_values + for id, d in pairs(Global.blackmarket_manager.grenades) do + table.insert(sort_data, {id, d}) + end + table.sort(sort_data, function(x, y) + xd = x[2] + yd = y[2] + x_td = m_tweak_data[x[1]] + y_td = m_tweak_data[y[1]] + if not xd.is_favorite ~= not yd.is_favorite then + return xd.is_favorite + end + if xd.unlocked ~= yd.unlocked then + return xd.unlocked + end + x_gv = x_td.global_value or x_td.dlc or "normal" + y_gv = y_td.global_value or y_td.dlc or "normal" + x_sn = l_tweak_data[x_gv] + y_sn = l_tweak_data[y_gv] + x_sn = x_sn and x_sn.sort_number or 1 + y_sn = y_sn and y_sn.sort_number or 1 + if x_sn ~= y_sn then + return x_sn < y_sn + end + return x[1] < y[1] + end +) + local max_items = math.ceil(#sort_data / (data.override_slots[1] or 3)) * (data.override_slots[1] or 3) + for i = 1, max_items do + data[i] = nil + end + local index = 0 + local guis_catalog, m_tweak_data, melee_weapon_id + for i, grenades_data in ipairs(sort_data) do + melee_weapon_id = grenades_data[1] + m_tweak_data = tweak_data.blackmarket.grenades[grenades_data[1]] or {} + guis_catalog = "guis/" + local bundle_folder = m_tweak_data.texture_bundle_folder + if bundle_folder then + guis_catalog = guis_catalog .. "dlcs/" .. tostring(bundle_folder) .. "/" + end + new_data = {} + new_data.name = melee_weapon_id + new_data.name_localized = managers.localization:text(tweak_data.blackmarket.grenades[new_data.name].name_id) + new_data.category = "grenades" + new_data.slot = i + new_data.unlocked = grenades_data[2].unlocked + new_data.equipped = grenades_data[2].equipped + new_data.level = grenades_data[2].level + new_data.stream = true + new_data.global_value = m_tweak_data.dlc or "normal" + new_data.skill_based = grenades_data[2].skill_based + new_data.skill_name = "bm_menu_skill_locked_" .. new_data.name + new_data.equipped_text = not new_data.unlocked and new_data.equipped and " " + if m_tweak_data and m_tweak_data.locks then + local dlc = m_tweak_data.locks.dlc + local achievement = m_tweak_data.locks.achievement + local saved_job_value = m_tweak_data.locks.saved_job_value + local level = m_tweak_data.locks.level + new_data.dlc_based = true + new_data.lock_texture = self:get_lock_icon(new_data, "guis/textures/pd2/lock_community") + if achievement and managers.achievment:get_info(achievement) and not managers.achievment:get_info(achievement).awarded then + new_data.dlc_locked = "menu_bm_achievement_locked_" .. tostring(achievement) + elseif dlc and not managers.dlc:is_dlc_unlocked(dlc) then + new_data.dlc_locked = tweak_data.lootdrop.global_values[dlc] and tweak_data.lootdrop.global_values[dlc].unlock_id or "bm_menu_dlc_locked" + else + new_data.dlc_locked = tweak_data.lootdrop.global_values[new_data.global_value].unlock_id or "bm_menu_dlc_locked" + end + else + new_data.lock_texture = self:get_lock_icon(new_data) + new_data.dlc_locked = tweak_data.lootdrop.global_values[new_data.global_value].unlock_id or "bm_menu_dlc_locked" + end + new_data.bitmap_texture = guis_catalog .. "textures/pd2/blackmarket/icons/grenades/" .. tostring(new_data.name) + if new_data.unlocked and not new_data.equipped then + table.insert(new_data, "lo_g_equip") + end + if new_data.unlocked and data.allow_preview and m_tweak_data.unit then + table.insert(new_data, "lo_g_preview") + end + + Hooks:Call("BlackMarketGUIOnPopulateGrenadesActionList", self, new_data) + + data[i] = new_data + index = i + end + for i = 1, max_items do + if not data[i] then + new_data = {} + new_data.name = "empty" + new_data.name_localized = "" + new_data.category = "grenades" + new_data.slot = i + new_data.unlocked = true + new_data.equipped = false + data[i] = new_data + end + end +end + Hooks:RegisterHook("BlackMarketGUIOnPopulateMasks") Hooks:RegisterHook("BlackMarketGUIOnPopulateMasksActionList") function BlackMarketGui.populate_masks(self, data) @@ -1591,6 +1700,14 @@ function BlackMarketGui._start_page_data(self) override_slots = {3, 3}, identifier = Idstring("melee_weapon") }) + table.insert(data, { + name = "bm_menu_grenades", + category = "grenades", + on_create_func_name = "populate_grenades", + allow_preview = true, + override_slots = {3, 3}, + identifier = self.identifiers.grenade + }) table.insert(data, { name = "bm_menu_armors", category = "armors", From ff0827abf5b6fe6107104a94cd7a39c5372adda3 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Mon, 30 Mar 2015 12:57:28 +1100 Subject: [PATCH 82/92] Fixed localization overriding the localization example hook --- GoonMod/req/localization.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GoonMod/req/localization.lua b/GoonMod/req/localization.lua index deb7815..7dde1bf 100644 --- a/GoonMod/req/localization.lua +++ b/GoonMod/req/localization.lua @@ -1,4 +1,4 @@ -Hooks:Add("LocalizationManagerPostInit", "LocalizationManagerPostInit_LocExample", function(loc) +Hooks:Add("LocalizationManagerPostInit", "LocalizationManagerPostInit_GoonMod", function(loc) loc:load_localization_file( GoonBase.LocalizationFolder .. "en.txt" ) end) From 93d6f4e8489ff62ee9f28a8125814108f5638335 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Mon, 30 Mar 2015 12:58:27 +1100 Subject: [PATCH 83/92] Fixed crash on exit game from preplanning screen while using mutators Added UpgradesTweakData hooks --- GoonMod/goonbase.lua | 2 ++ GoonMod/lua/NetworkManager.lua | 39 +++++++++++++++++++++++++++++++ GoonMod/lua/UpgradesTweakData.lua | 14 +++++++++++ 3 files changed, 55 insertions(+) create mode 100644 GoonMod/lua/NetworkManager.lua create mode 100644 GoonMod/lua/UpgradesTweakData.lua diff --git a/GoonMod/goonbase.lua b/GoonMod/goonbase.lua index 7c42abc..c1dbf68 100644 --- a/GoonMod/goonbase.lua +++ b/GoonMod/goonbase.lua @@ -86,6 +86,8 @@ GoonBase.HookFiles = { ["lib/managers/skilltreemanager"] = "SkillTreeManager.lua", ["lib/managers/menu/skilltreegui"] = "SkillTreeGUI.lua", ["lib/network/networkgame"] = "NetworkGame.lua", + ["lib/tweak_data/upgradestweakdata"] = "UpgradesTweakData.lua", + ["lib/network/base/networkmanager"] = "NetworkManager.lua", } diff --git a/GoonMod/lua/NetworkManager.lua b/GoonMod/lua/NetworkManager.lua new file mode 100644 index 0000000..d3f4a2f --- /dev/null +++ b/GoonMod/lua/NetworkManager.lua @@ -0,0 +1,39 @@ + +CloneClass( NetworkManager ) + +function NetworkManager.stop_network( self, clean ) + if self._started then + self._game:on_network_stopped() + self._started = false + if clean and self._session then + local peers = self._session:peers() + for k, peer in pairs(peers) do + local rpc = peer:rpc() + if rpc then + Network:reset_connection(rpc) + Network:remove_client(rpc) + end + end + end + self._handlers = nil + self._shared_handler_data = nil + self._session:destroy() + + -- TODO: Find the part in PlayerManager that is attempting to use the session after it's already been destroyed + -- until then, don't set it to nil and hope that changing levels cleans it up properly + -- FATAL ERROR: [string "lib/managers/playermanager.lua"]:1818: attempt to index a nil value + -- self._session = nil + + self._game = nil + self._stop_network = nil + self._stop_next_frame = nil + self._network_bound = nil + Network:unbind() + Network:set_disconnected() + if not Application:editor() then + Network:set_multiplayer(false) + end + cat_print("multiplayer_base", "[NetworkManager:stop_network]") + print("---------------------------------------------------------") + end +end diff --git a/GoonMod/lua/UpgradesTweakData.lua b/GoonMod/lua/UpgradesTweakData.lua new file mode 100644 index 0000000..3ab00d3 --- /dev/null +++ b/GoonMod/lua/UpgradesTweakData.lua @@ -0,0 +1,14 @@ + +CloneClass( UpgradesTweakData ) + +Hooks:RegisterHook("UpgradesTweakDataOnSetupPaydayValues") +function UpgradesTweakData._init_pd2_values( self ) + self.orig._init_pd2_values( self ) + Hooks:Call("UpgradesTweakDataOnSetupPaydayValues", self) +end + +Hooks:RegisterHook("UpgradesTweakDataOnSetupPlayerDefinitions") +function UpgradesTweakData._player_definitions( self ) + self.orig._player_definitions( self ) + Hooks:Call("UpgradesTweakDataOnSetupPlayerDefinitions", self) +end From 1ade97ba9872b5bfa94edd73e4ef0a7cacebd5b7 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Mon, 30 Mar 2015 12:58:46 +1100 Subject: [PATCH 84/92] Removed unused localization keys --- GoonMod/loc/en.txt | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/GoonMod/loc/en.txt b/GoonMod/loc/en.txt index 814e08e..207db13 100644 --- a/GoonMod/loc/en.txt +++ b/GoonMod/loc/en.txt @@ -191,10 +191,7 @@ "OptionsMenu_InspectWeaponDesc" : "Key to press to inspect your weapon while in-game", "OptionsMenu_CycleStattrakMode" : "Cycle Stat-trak Modes", "OptionsMenu_CycleStattrakModeDesc" : "Key to cycle through available kill modes on your weapons", - - "TrainHeist_PlansInv" : "Train Intel", - "TrainHeist_PlansInvDesc" : "Details of a train transporting an experimental turret. This unlocks the Train Transport heist in Crime.net while you have intel in reserve.\n\nFound in an Armoured Transport. Will be consumed upon successful completion of the heist.", - + "gm_options_normalized_sensitivity_title" : "Enabled Ironsight Normalized Sensitivity", "gm_options_normalized_sensitivity_desc" : "Lower the sensitivity when using ironsights to more accurately place your shots.", @@ -214,7 +211,7 @@ "gm_options_wc_clear_melee_data_desc" : "Erase all of your customization data from all of your melee weapons", "bm_mtl_no_material" : "No Material", - "WeaponCustomization_MenuItem" : "Customize Weapon", + "bm_menu_customize_weapon" : "Customize Weapon", "bm_menu_customize_weapon_title" : "Customize Weapon: $weapon_name", "wc_modifying_parts" : "Modifying Parts", "wc_not_modifying_parts" : "Not Modifying Parts", @@ -235,15 +232,6 @@ "wc_mod_overrides_not_installed_dont_show" : "I Know, Don't Show This Again", "wc_mod_overrides_not_installed_cancel" : "Later", - "Options_EnemyLaserName" : "Enemy Laser Color", - "Options_EnemyLaserDesc" : "Modify the color of enemy's weapons lasers", - "Options_EnemyLaserEnableTitle" : "Enable Custom Enemy Laser Color", - "Options_EnemyLaserEnableDesc" : "Use the custom set color for enemy's weapons Lasers", - "Options_EnemyLaserRainbowTitle" : "Enable Rainbow Laser", - "Options_EnemyLaserRainbowDesc" : "Enable rainbow instead of the set Hue", - "Options_EnemyLaserRainbowSpeedTitle" : "Rainbow Speed", - "Options_EnemyLaserRainbowSpeedDesc" : "Set the speed of the rainbow effect", - "gm_options_custom_use_hue_title" : "Use HSV Colour Space", "gm_options_custom_use_hue_desc" : "Use the HSV colour space instead of the RGB", "gm_options_custom_rh_title" : "Red/Hue", From 587e45a877a5d898938697d39499d08f4bcd26a2 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Mon, 30 Mar 2015 13:00:04 +1100 Subject: [PATCH 85/92] Fixed StartTheGame override on MenuCallbackHandler not passing through the handler reference --- GoonMod/lua/MenuManager.lua | 7 ++++--- GoonMod/mod.txt | 4 +++- GoonMod/mods/weapon_customization_menus.lua | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/GoonMod/lua/MenuManager.lua b/GoonMod/lua/MenuManager.lua index 8a873dc..d4a81f3 100644 --- a/GoonMod/lua/MenuManager.lua +++ b/GoonMod/lua/MenuManager.lua @@ -13,8 +13,8 @@ Hooks:RegisterHook("MenuCallbackHandlerPreStartTheGame") function MenuCallbackHandler.start_the_game(self) if not self._start_the_game then - self._start_the_game = function() - self.orig.start_the_game() + self._start_the_game = function(self) + self.orig.start_the_game(self) end end @@ -24,7 +24,8 @@ function MenuCallbackHandler.start_the_game(self) return nil end - self.orig.start_the_game(self) + return self.orig.start_the_game(self) + end function MenuCallbackHandler:_process_start_game_delay( t, dt ) diff --git a/GoonMod/mod.txt b/GoonMod/mod.txt index a3d4485..516c827 100644 --- a/GoonMod/mod.txt +++ b/GoonMod/mod.txt @@ -72,6 +72,8 @@ { "hook_id" : "lib/managers/experiencemanager", "script_path" : "goonbase.lua" }, { "hook_id" : "lib/managers/skilltreemanager", "script_path" : "goonbase.lua" }, { "hook_id" : "lib/managers/menu/skilltreegui", "script_path" : "goonbase.lua" }, - { "hook_id" : "lib/network/networkgame", "script_path" : "goonbase.lua" } + { "hook_id" : "lib/network/networkgame", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/tweak_data/upgradestweakdata", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/network/base/networkmanager", "script_path" : "goonbase.lua" } ] } diff --git a/GoonMod/mods/weapon_customization_menus.lua b/GoonMod/mods/weapon_customization_menus.lua index baddfec..c7d24d0 100644 --- a/GoonMod/mods/weapon_customization_menus.lua +++ b/GoonMod/mods/weapon_customization_menus.lua @@ -252,7 +252,7 @@ Hooks:Add("BlackMarketGUIPostSetup", "BlackMarketGUIPostSetup_WeaponCustomizatio prio = 5, btn = "BTN_BACK", pc_btn = Idstring("toggle_chat"), - name = "WeaponCustomization_MenuItem", + name = "bm_menu_customize_weapon", callback = callback(gui, gui, "customize_weapon_visuals") } From 76d57eb881a881e1d0836cf2075b897d338e0579 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Mon, 30 Mar 2015 13:37:24 +1100 Subject: [PATCH 86/92] Fixed Addicts mutator for BLT changes --- GoonMod/mutators/mutator_addicts.lua | 54 +++++++++++++--------------- 1 file changed, 24 insertions(+), 30 deletions(-) diff --git a/GoonMod/mutators/mutator_addicts.lua b/GoonMod/mutators/mutator_addicts.lua index fe8d074..62c301d 100644 --- a/GoonMod/mutators/mutator_addicts.lua +++ b/GoonMod/mutators/mutator_addicts.lua @@ -60,15 +60,16 @@ function Mutator:OnEnabled() end - end) Hooks:Add("PlayerDamageOnPostInit", self._playerDamageOnPostInitHook, function(ply, unit) - Queue:Add("AddictsSpawnFreeDrugs", function() + + DelayedCalls:Add("AddictsSpawnFreeDrugs", 1, function() local loot = self._drug_spawns[math.random(1, #self._drug_spawns)] managers.player:force_verify_carry() self:SpawnMutatorLoot( loot ) - end, 1) + end) + end) end @@ -80,37 +81,30 @@ end function Mutator:SpawnMutatorLoot(loot_id, zipline_unit) - local psuccess, perror = pcall(function() - - local carry_data = tweak_data.carry[loot_id] - if not carry_data then - return - end - - local player = managers.player:player_unit() - if player then - player:sound():play("Play_bag_generic_throw", nil, false) - else - return - end + local carry_data = tweak_data.carry[loot_id] + if not carry_data then + return + end - local camera_ext = player:camera() - local dye_initiated = carry_data.dye_initiated - local has_dye_pack = carry_data.has_dye_pack - local dye_value_multiplier = carry_data.dye_value_multiplier - local throw_distance_multiplier_upgrade_level = managers.player:upgrade_level("carry", "throw_distance_multiplier", 0) + local player = managers.player:player_unit() + if player then + player:sound():play("Play_bag_generic_throw", nil, false) + else + return + end - local pos = camera_ext:position() - local rot = camera_ext:rotation() - local peer_id = managers.network:session():local_peer() or 0 + local camera_ext = player:camera() + local dye_initiated = carry_data.dye_initiated + local has_dye_pack = carry_data.has_dye_pack + local dye_value_multiplier = carry_data.dye_value_multiplier + local throw_distance_multiplier_upgrade_level = managers.player:upgrade_level("carry", "throw_distance_multiplier", 0) - if not Network:is_client() then - managers.player:server_drop_carry(loot_id, carry_data.multiplier, dye_initiated, has_dye_pack, dye_value_multiplier, pos, rot, player:camera():forward(), throw_distance_multiplier_upgrade_level, zipline_unit, managers.network:session():local_peer():id()) - end + local pos = camera_ext:position() + local rot = camera_ext:rotation() + local peer_id = managers.network:session():local_peer() or 0 - end) - if not psuccess then - Print("[Error] " .. perror) + if not Network:is_client() then + managers.player:server_drop_carry(loot_id, carry_data.multiplier, dye_initiated, has_dye_pack, dye_value_multiplier, pos, rot, player:camera():forward(), throw_distance_multiplier_upgrade_level, zipline_unit, managers.network:session():local_peer():id()) end end From c84f4ce3941fb71368ff2341d2f2e8d484f869c2 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Mon, 30 Mar 2015 13:38:21 +1100 Subject: [PATCH 87/92] Fixed exploding bullets mutator for BLT --- GoonMod/mutators/mutator_exploding_bullets.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/GoonMod/mutators/mutator_exploding_bullets.lua b/GoonMod/mutators/mutator_exploding_bullets.lua index 1ce27e2..3f738a7 100644 --- a/GoonMod/mutators/mutator_exploding_bullets.lua +++ b/GoonMod/mutators/mutator_exploding_bullets.lua @@ -37,6 +37,7 @@ function Mutator:OnEnabled() InstantExplosiveBulletBase.on_collision(bullet, col_ray, weapon_unit, user_unit, damage, blank) end InstantBulletBase.on_collision_client = InstantExplosiveBulletBase.on_collision_client + InstantBulletBase.on_collision_server = InstantExplosiveBulletBase.on_collision_server end) From 2ebf75941c970a1abcf830883811b1bd34195315 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Mon, 30 Mar 2015 14:09:14 +1100 Subject: [PATCH 88/92] Game will start once all modded clients have reported mutator status instead of waiting for the timeout Mutators will be loaded from the folder instead of per file name Mutators menu will darken background when opened Mutators uses the new localization system --- GoonMod/mods/{disabled => }/mutators.lua | 182 +++++++++++++++-------- 1 file changed, 116 insertions(+), 66 deletions(-) rename GoonMod/mods/{disabled => }/mutators.lua (84%) diff --git a/GoonMod/mods/disabled/mutators.lua b/GoonMod/mods/mutators.lua similarity index 84% rename from GoonMod/mods/disabled/mutators.lua rename to GoonMod/mods/mutators.lua index 75dbadc..29341f6 100644 --- a/GoonMod/mods/disabled/mutators.lua +++ b/GoonMod/mods/mutators.lua @@ -24,6 +24,7 @@ Mutators.LoadedMutators = Mutators.LoadedMutators or {} Mutators.ActiveMutators = Mutators.ActiveMutators or {} Mutators.ClientMutatorCheck = Mutators.ClientMutatorCheck or {} Mutators.NetworkTimeoutTime = 3 +Mutators.MutatorUnreportedStatus = "unreported" -- Network Mutators.Network = {} @@ -35,34 +36,7 @@ Mutators.Network.MutatorCheckSuccess = "CheckMutatorSuccess" Mutators.Network.MutatorCheckFailure = "CheckMutatorFailure" -- Paths -Mutators.MutatorsPath = "/" -Mutators.MutatorsList = { - -- #POPULATE mutators - -- #DEBUG_ONLY - "mutators/base_mutator.lua", - "mutators/mutator_all_cloakers.lua", - "mutators/mutator_all_shields.lua", - "mutators/mutator_all_tazers.lua", - "mutators/mutator_all_bulldozers.lua", - -- "mutators/mutator_all_gangsters.lua", - "mutators/mutator_lightning_speed.lua", - "mutators/mutator_insane_spawnrate.lua", - "mutators/mutator_insane_spawnrate_cops.lua", - "mutators/mutator_suicidal_spawnrate.lua", - "mutators/mutator_suicidal_spawnrate_cops.lua", - "mutators/mutator_shielddozers.lua", - -- "mutators/mutator_tank_cloakers.lua", - "mutators/mutator_realism_mode.lua", - "mutators/mutator_exploding_bullets.lua", - "mutators/mutator_unbreakable.lua", - "mutators/mutator_suicide_cloakers.lua", - "mutators/mutator_instagib.lua", - "mutators/mutator_jamming_weapons.lua", - "mutators/mutator_addicts.lua", - "mutators/mutator_floating_bodies.lua", - "mutators/mutator_no_ammo_pickups.lua", - -- #END -} +Mutators.MutatorsPath = GoonBase.Path .. "mutators/" Mutators.MenuPrefix = "toggle_mutator_" -- Options @@ -128,7 +102,6 @@ Hooks:Add("MenuManagerSetupCustomMenus", "MenuManagerSetupCustomMenus_MutatorsMe priority = 1000, }) - -- Add mutators to menu Mutators:AddLoadedMutatorsToMenu() @@ -136,9 +109,20 @@ end) Hooks:Add("MenuManagerBuildCustomMenus", "MenuManagerBuildCustomMenus_MutatorsMenu", function(menu_manager, menu_nodes) + MenuCallbackHandler.GoonModFocusMutatorsMenu = function( node, focus ) + if focus then + Mutators:ShowMutatorsMenu() + else + Mutators:HideMutatorsMenu() + end + end + -- Build menu local menu_id = Mutators.MenuID - menu_nodes[menu_id] = MenuHelper:BuildMenu( menu_id ) + local menu_data = { + focus_changed_callback = "GoonModFocusMutatorsMenu" + } + menu_nodes[menu_id] = MenuHelper:BuildMenu( menu_id, menu_data ) -- Add to main menu and lobby only if menu_nodes.main ~= nil then @@ -159,6 +143,34 @@ Hooks:Add("MenuManagerBuildCustomMenus", "MenuManagerBuildCustomMenus_MutatorsMe end) -- Mutators Functions +function Mutators:ShowMutatorsMenu() + + if not managers.menu_component or not managers.gui_data then + return + end + if managers.menu_component._contract_gui then + managers.menu_component:close_contract_gui() + end + + self._fullscreen_ws = self._fullscreen_ws or managers.gui_data:create_fullscreen_16_9_workspace() + if not self._darken_bg then + self._darken_bg = self._fullscreen_ws:panel():rect({ + color = Color.black:with_alpha(0.4), + layer = 50 + }) + end + self._darken_bg:set_alpha(1) + +end + +function Mutators:HideMutatorsMenu() + + if self._darken_bg then + self._darken_bg:set_alpha(0) + end + +end + function Mutators:ShowHelpMenu() local title = managers.localization:text("Mutators_HelpTitle") @@ -171,8 +183,11 @@ end function Mutators:LoadMutators() - for k, v in pairs( self.MutatorsList ) do - SafeDoFile( GoonBase.Path .. self.MutatorsPath .. v ) + local files = file.GetFiles( Mutators.MutatorsPath ) + if files then + for k, v in pairs( files ) do + SafeDoFile( Mutators.MutatorsPath .. v ) + end end end @@ -188,9 +203,30 @@ function Mutators:SetupMutatorsLocalization() end end +function Mutators:RegisterLocalization( key, text ) + + if not Mutators._cached_localization then + Mutators._cached_localization = {} + end + Mutators._cached_localization[key] = text + +end + +Hooks:Add("LocalizationManagerPostInit", "LocalizationManagerPostInit_" .. Mod:ID(), function(loc) + + for k, v in pairs( Mutators._cached_localization ) do + loc:add_localized_strings({ + [k] = v, + }) + end + + Mutators._cached_localization = {} + +end) + function Mutators:SetupMutators() - if Global.game_settings and Global.game_settings.active_mutators then + if Global and Global.game_settings and Global.game_settings.active_mutators then for k, v in pairs( Global.game_settings.active_mutators ) do if v and Mutators.LoadedMutators[k] then @@ -198,7 +234,6 @@ function Mutators:SetupMutators() end end - return else for k, v in pairs( Mutators.LoadedMutators ) do @@ -487,15 +522,15 @@ Hooks:Add( "MenuCallbackHandlerPreStartTheGame", "MenuCallbackHandlerPreStartThe end end - if not GoonBase.Network:IsMultiplayer() or ( GoonBase.Network:IsMultiplayer() and GoonBase.Network:IsHost() ) then + if not LuaNetworking:IsMultiplayer() or ( LuaNetworking:IsMultiplayer() and LuaNetworking:IsHost() ) then Mutators:AddRandomizedMutations() end - if Global.game_settings then + if Global and Global.game_settings then Global.game_settings.active_mutators = {} for k, v in pairs( Mutators.ActiveMutators ) do - Global.game_settings.active_mutators[k] = v + Global.game_settings.active_mutators[k] = true end end @@ -511,7 +546,13 @@ Hooks:Add( "MenuCallbackHandlerPreStartTheGame", "MenuCallbackHandlerPreStartThe Mutators:ShowNetworkingMutatorsWindow() for k, v in pairs( Mutators.ActiveMutators ) do + Mutators.ClientMutatorCheck[k] = {} + for x, y in pairs( LuaNetworking:GetPeers() ) do + local client_id = y:id() + Mutators.ClientMutatorCheck[k][client_id] = Mutators.MutatorUnreportedStatus + end + Mutators:SendNetworkedMutatorToClients( k, true ) Mutators:CheckIfClientsHaveMutator( k ) end @@ -524,7 +565,7 @@ end ) function Mutators:CheckNetworkMutators( callback_handler ) if Global.game_settings and not Global.game_settings.single_player then - if GoonBase.Network:IsMultiplayer() and GoonBase.Network:IsHost() and GoonBase.Network:GetNumberOfPeers() > 0 then + if LuaNetworking:IsMultiplayer() and LuaNetworking:IsHost() and LuaNetworking:GetNumberOfPeers() > 0 then return true end end @@ -535,23 +576,14 @@ function Mutators:ShowNetworkingMutatorsWindow() local title = managers.localization:text("NetworkedMutators_SendingData_Title") local message = managers.localization:text("NetworkedMutators_SendingData_Message") - local menuOptions = {} - menuOptions[1] = { + local options = {} + options[1] = { text = managers.localization:text("NetworkedMutators_SendingData_Cancel"), callback = Mutators.NetworkingMutatorsCancel, is_cancel_button = true } - -- #DEBUG_ONLY - menuOptions[2] = { - text = managers.localization:text("NetworkedMutators_SendingData_DebugForce"), - callback = Mutators.DebugForceStartGame, - } - menuOptions[3] = { - text = managers.localization:text("NetworkedMutators_SendingData_DebugRelease"), - callback = Mutators.DebugReleaseStartDelay, - } - -- #END - local menu = QuickMenu:new(title, message, menuOptions) + + local menu = QuickMenu:new( title, message, options ) menu.dialog_data.indicator = true menu:Show() @@ -645,25 +677,25 @@ Hooks:Add("NetworkReceivedData", "NetworkReceivedData_" .. Mod:ID(), function(se end) function Mutators:ClearClientsNetworkedMutators() - GoonBase.Network:SendToPeers( Mutators.Network.ClearMutators, "" ) + LuaNetworking:SendToPeers( Mutators.Network.ClearMutators, "" ) end function Mutators:ClearNetworkedMutators() - if Global.game_settings then + if Global and Global.game_settings then Global.game_settings.active_mutators = {} end end function Mutators:SendNetworkedMutatorToClients( mutator_id, enabled ) if enabled then - GoonBase.Network:SendToPeers( Mutators.Network.EnableMutator, mutator_id ) + LuaNetworking:SendToPeers( Mutators.Network.EnableMutator, mutator_id ) else - GoonBase.Network:SendToPeers( Mutators.Network.DisableMutator, mutator_id ) + LuaNetworking:SendToPeers( Mutators.Network.DisableMutator, mutator_id ) end end function Mutators:SetNetworkedMutator( mutator_id, enable ) - if Global.game_settings then + if Global and Global.game_settings then if not Global.game_settings.active_mutators then Global.game_settings.active_mutators = {} end @@ -672,15 +704,15 @@ function Mutators:SetNetworkedMutator( mutator_id, enable ) end function Mutators:CheckIfClientsHaveMutator( mutator_id ) - GoonBase.Network:SendToPeers( Mutators.Network.MutatorCheck, mutator_id ) + LuaNetworking:SendToPeers( Mutators.Network.MutatorCheck, mutator_id ) end function Mutators:CheckIfHasMutator( sender, mutator_id ) if Mutators.LoadedMutators[ mutator_id ] == nil then - GoonBase.Network:SendToPeer( sender, Mutators.Network.MutatorCheckFailure, mutator_id ) + LuaNetworking:SendToPeer( sender, Mutators.Network.MutatorCheckFailure, mutator_id ) else - GoonBase.Network:SendToPeer( sender, Mutators.Network.MutatorCheckSuccess, mutator_id ) + LuaNetworking:SendToPeer( sender, Mutators.Network.MutatorCheckSuccess, mutator_id ) end end @@ -699,6 +731,21 @@ function Mutators:MarkClientHasMutator( client, mutator_id, has_mutator ) end +function Mutators:CheckAllClientsHaveReported() + + for mutator_id, mutator in pairs( self.ClientMutatorCheck ) do + for k, v in pairs( LuaNetworking:GetPeers() ) do + local client_id = v:id() + if mutator[ client_id ] == Mutators.MutatorUnreportedStatus then + return false + end + end + end + + return true + +end + Hooks:Add("MenuUpdate", "MenuUpdate_" .. Mod:ID(), function(t, dt) Mutators:CheckMutatorTimeout() end) @@ -710,11 +757,14 @@ end) function Mutators:CheckMutatorTimeout() if self._game_delay_time then + local t = Application:time() - self._game_delay_time - if t > self.NetworkTimeoutTime then + + if t > self.NetworkTimeoutTime or self:CheckAllClientsHaveReported() then self:CheckAllClientsHaveMutators() self._game_delay_time = nil end + end end @@ -741,27 +791,27 @@ function Mutators:CheckAllClientsHaveMutators() if not string.is_nil_or_empty( added_missing_mutator_text ) then missing_mutator_text = missing_mutator_text .. "\n" .. added_missing_mutator_text .. ": " - missing_mutator_text = missing_mutator_text .. GoonBase.Network:GetNameFromPeerID(client_id) + missing_mutator_text = missing_mutator_text .. LuaNetworking:GetNameFromPeerID(client_id) added_missing_mutator_text = "" else - missing_mutator_text = missing_mutator_text .. ", " .. GoonBase.Network:GetNameFromPeerID(client_id) + missing_mutator_text = missing_mutator_text .. ", " .. LuaNetworking:GetNameFromPeerID(client_id) end end end - for k, v in pairs( GoonBase.Network:GetPeers() ) do + for k, v in pairs( LuaNetworking:GetPeers() ) do local client_id = v:id() - if not mutator[ client_id ] then + if not mutator[ client_id ] or mutator[ client_id ] == Mutators.MutatorUnreportedStatus then missing_mutator = true if not string.is_nil_or_empty( added_missing_mutator_text ) then missing_mutator_text = missing_mutator_text .. "\n" .. added_missing_mutator_text .. ": " - missing_mutator_text = missing_mutator_text .. GoonBase.Network:GetNameFromPeerID(client_id) + missing_mutator_text = missing_mutator_text .. LuaNetworking:GetNameFromPeerID(client_id) added_missing_mutator_text = "" else - missing_mutator_text = missing_mutator_text .. ", " .. GoonBase.Network:GetNameFromPeerID(client_id) + missing_mutator_text = missing_mutator_text .. ", " .. LuaNetworking:GetNameFromPeerID(client_id) end end From 080f1e58b32bc68ae3c256d90acb43d387e71778 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Mon, 30 Mar 2015 14:16:04 +1100 Subject: [PATCH 89/92] Updated base mutator IsEnabled Removed old mutators --- GoonMod/goonbase.lua | 2 - GoonMod/mod.txt | 1 - GoonMod/mutators/base_mutator.lua | 21 ++++--- GoonMod/mutators/mutator_all_gangsters.lua | 73 ---------------------- GoonMod/mutators/mutator_tank_cloakers.lua | 26 -------- 5 files changed, 11 insertions(+), 112 deletions(-) delete mode 100644 GoonMod/mutators/mutator_all_gangsters.lua delete mode 100644 GoonMod/mutators/mutator_tank_cloakers.lua diff --git a/GoonMod/goonbase.lua b/GoonMod/goonbase.lua index c1dbf68..f54b5b5 100644 --- a/GoonMod/goonbase.lua +++ b/GoonMod/goonbase.lua @@ -77,9 +77,7 @@ GoonBase.HookFiles = { ["lib/tweak_data/weapontweakdata"] = "WeaponTweakData.lua", ["lib/units/beings/player/playerinventory"] = "PlayerInventory.lua", ["lib/tweak_data/skilltreetweakdata"] = "SkillTreeTweakData.lua", - -- ["lib/managers/gameplaycentralmanager"] = "GameplayCentralManager.lua", ["lib/managers/systemmenumanager"] = "SystemMenuManager.lua", - -- ["lib/managers/challengemanager"] = "ChallengeManager.lua", ["lib/managers/menu/playerprofileguiobject"] = "PlayerProfileGUIObject.lua", ["lib/managers/menu/walletguiobject"] = "WalletGUIObject.lua", ["lib/managers/experiencemanager"] = "ExperienceManager.lua", diff --git a/GoonMod/mod.txt b/GoonMod/mod.txt index 516c827..a7d349c 100644 --- a/GoonMod/mod.txt +++ b/GoonMod/mod.txt @@ -66,7 +66,6 @@ { "hook_id" : "lib/units/beings/player/playerinventory", "script_path" : "goonbase.lua" }, { "hook_id" : "lib/tweak_data/skilltreetweakdata", "script_path" : "goonbase.lua" }, { "hook_id" : "lib/managers/systemmenumanager", "script_path" : "goonbase.lua" }, - { "hook_id" : "lib/managers/challengemanager", "script_path" : "goonbase.lua" }, { "hook_id" : "lib/managers/menu/playerprofileguiobject", "script_path" : "goonbase.lua" }, { "hook_id" : "lib/managers/menu/walletguiobject", "script_path" : "goonbase.lua" }, { "hook_id" : "lib/managers/experiencemanager", "script_path" : "goonbase.lua" }, diff --git a/GoonMod/mutators/base_mutator.lua b/GoonMod/mutators/base_mutator.lua index ba68458..9c225a6 100644 --- a/GoonMod/mutators/base_mutator.lua +++ b/GoonMod/mutators/base_mutator.lua @@ -184,17 +184,20 @@ end function BaseMutator:IsEnabled() local Net = _G.LuaNetworking - if Net:IsMultiplayer() and not Net:IsHost() then - return false - end + if Global and Global.game_settings then - if Global.game_settings ~= nil then if Net:IsMultiplayer() and Global.game_settings.permission == "public" then return false end + if Global.game_settings.active_mutators then - return Global.game_settings.active_mutators[self.Id] or false + return Global.game_settings.active_mutators[ self:ID() ] or false + else + if Net:IsMultiplayer() and not Net:IsHost() then + return false + end end + end return false @@ -238,9 +241,7 @@ function BaseMutator:OnDisabled() end function BaseMutator:_UpdateMatchmaking() - local psuccess, perror = pcall(function() - if MenuCallbackHandler then - MenuCallbackHandler:update_matchmake_attributes() - end - end) + if MenuCallbackHandler and MenuCallbackHandler.update_matchmake_attributes then + MenuCallbackHandler:update_matchmake_attributes() + end end diff --git a/GoonMod/mutators/mutator_all_gangsters.lua b/GoonMod/mutators/mutator_all_gangsters.lua deleted file mode 100644 index 2ef05bd..0000000 --- a/GoonMod/mutators/mutator_all_gangsters.lua +++ /dev/null @@ -1,73 +0,0 @@ - -local Mutator = class(BaseMutator) -Mutator.Id = "AllGangsterSpawns" -Mutator.OptionsName = "Gangsters Only" -Mutator.OptionsDesc = "Replace all spawning units with various gangsters" -Mutator.Incompatibilities = { "AllCloakerSpawns", "AllBulldozerSpawns", "AllShieldSpawns" } - -Mutator.HookTaskData = "GroupAITweakDataPostInitTaskData_" .. Mutator.Id -Mutator.HookUnitCategories = "GroupAITweakDataPostInitUnitCategories_" .. Mutator.Id - -Hooks:Add("GoonBaseRegisterMutators", "GoonBaseRegisterMutators_" .. Mutator.Id, function() - GoonBase.Mutators:RegisterMutator( Mutator ) -end) - -function Mutator:OnEnabled() - - Hooks:Add("GroupAITweakDataPostInitUnitCategories", self.HookUnitCategories, function(data, difficulty_index) - self:ModifyUnitCategories(data, difficulty_index) - end) - -end - -function Mutator:OnDisabled() - Hooks:Remove(self.HookUnitCategories) -end - -function Mutator:ModifyUnitCategories(data, difficulty_index) - - local gang_mexican = { - Idstring("units/payday2/characters/ene_gang_mexican_1/ene_gang_mexican_1"), - Idstring("units/payday2/characters/ene_gang_mexican_2/ene_gang_mexican_2"), - Idstring("units/payday2/characters/ene_gang_mexican_3/ene_gang_mexican_3"), - Idstring("units/payday2/characters/ene_gang_mexican_4/ene_gang_mexican_4"), - } - local gang_russian = { - Idstring("units/payday2/characters/ene_gang_russian_1/ene_gang_russian_1"), - Idstring("units/payday2/characters/ene_gang_russian_2/ene_gang_russian_2"), - Idstring("units/payday2/characters/ene_gang_russian_3/ene_gang_russian_3"), - Idstring("units/payday2/characters/ene_gang_russian_4/ene_gang_russian_4"), - Idstring("units/payday2/characters/ene_gang_russian_5/ene_gang_russian_5"), - } - local gang_mobster = { - Idstring("units/payday2/characters/ene_gang_mobster_1/ene_gang_mobster_1"), - Idstring("units/payday2/characters/ene_gang_mobster_2/ene_gang_mobster_2"), - Idstring("units/payday2/characters/ene_gang_mobster_3/ene_gang_mobster_3"), - Idstring("units/payday2/characters/ene_gang_mobster_4/ene_gang_mobster_4"), - } - - data.unit_categories.CS_tazer.units = { - gang_mexican[math.random(1, #gang_mexican)], - gang_russian[math.random(1, #gang_russian)], - gang_russian[math.random(1, #gang_russian)], - -- gang_mobster[math.random(1, #gang_mobster)], - -- gang_mobster[math.random(1, #gang_mobster)], - } - data.unit_categories.CS_cop_C45_R870 = data.unit_categories.CS_tazer - data.unit_categories.CS_cop_stealth_MP5 = data.unit_categories.CS_tazer - data.unit_categories.CS_swat_MP5 = data.unit_categories.CS_tazer - data.unit_categories.CS_swat_R870 = data.unit_categories.CS_tazer - data.unit_categories.CS_heavy_M4 = data.unit_categories.CS_tazer - data.unit_categories.CS_heavy_M4_w = data.unit_categories.CS_tazer - data.unit_categories.CS_shield = data.unit_categories.CS_tazer - data.unit_categories.FBI_suit_C45_M4 = data.unit_categories.CS_tazer - data.unit_categories.FBI_suit_M4_MP5 = data.unit_categories.CS_tazer - data.unit_categories.FBI_suit_stealth_MP5 = data.unit_categories.CS_tazer - data.unit_categories.FBI_swat_M4 = data.unit_categories.CS_tazer - data.unit_categories.FBI_swat_R870 = data.unit_categories.CS_tazer - data.unit_categories.FBI_heavy_G36 = data.unit_categories.CS_tazer - data.unit_categories.FBI_heavy_G36_w = data.unit_categories.CS_tazer - data.unit_categories.FBI_shield = data.unit_categories.CS_tazer - data.unit_categories.FBI_tank = data.unit_categories.CS_tazer - -end diff --git a/GoonMod/mutators/mutator_tank_cloakers.lua b/GoonMod/mutators/mutator_tank_cloakers.lua deleted file mode 100644 index 3af90b9..0000000 --- a/GoonMod/mutators/mutator_tank_cloakers.lua +++ /dev/null @@ -1,26 +0,0 @@ - -local Mutator = class(BaseMutator) -Mutator.Id = "BulldozerCloakers" -Mutator.OptionsName = "Bulldozers are Cloakers" -Mutator.OptionsDesc = "Bulldozers will charge and jump kick you" -Mutator.AllPlayersRequireMod = true - -Mutator.HookTankData = "CharacterTweakDataPostInitTank_BulldozerCloakers" - -Hooks:Add("GoonBaseRegisterMutators", "GoonBaseRegisterMutators_AllTaserSpawns", function() - GoonBase.Mutators:RegisterMutator( Mutator ) -end) - -function Mutator:OnEnabled() - - Hooks:Add("CharacterTweakDataPostInitTank", self.HookTankData, function(data, presets) - data.spooc.spooc_attack_timeout = {10, 10} - data.spooc.spooc_attack_beating_time = {3, 3} - data.spooc.spooc_attack_use_smoke_chance = 1 - end) - -end - -function Mutator:OnDisabled() - Hooks:Remove(self.HookTankData) -end From ad8379b3e46f1dbca15849ece021674bb780154e Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Tue, 31 Mar 2015 23:18:53 +1100 Subject: [PATCH 90/92] Removed disabled mods and unused code --- GoonMod/lua/GroupAIStateBesiege.lua | 521 ------ GoonMod/mods/disabled/stat_trak.lua | 372 ---- .../disabled/stat_trak_weapon_offsets.lua | 62 - GoonMod/mods/disabled/trading.lua | 1497 ----------------- 4 files changed, 2452 deletions(-) delete mode 100644 GoonMod/mods/disabled/stat_trak.lua delete mode 100644 GoonMod/mods/disabled/stat_trak_weapon_offsets.lua delete mode 100644 GoonMod/mods/disabled/trading.lua diff --git a/GoonMod/lua/GroupAIStateBesiege.lua b/GoonMod/lua/GroupAIStateBesiege.lua index 2301e2a..c48e8aa 100644 --- a/GoonMod/lua/GroupAIStateBesiege.lua +++ b/GoonMod/lua/GroupAIStateBesiege.lua @@ -6,524 +6,3 @@ function GroupAIStateBesiege.init(self) self.orig.init(self) Hooks:Call("GroupAIStateBesiegeInit", self) end - ---[[ -function GroupAIStateBesiege:_assign_groups_to_retire(allowed_groups, suitable_grp_func) - -- Never back down -end - -function GroupAIStateBesiege:_upd_group_spawning() - - -- Print("Update spawn groups, num groups: ", #self._spawning_groups) - for k, v in pairs( self._spawning_groups ) do - self:_update_individual_group_spawning(k) - end - -end - -function GroupAIStateBesiege:_queue_police_upd_task() - self._police_upd_task_queued = true - managers.enemy:queue_task("GroupAIStateBesiege._upd_police_activity", GroupAIStateBesiege._upd_police_activity, self, self._t + (next(self._spawning_groups) and 0.0166 or 2)) -end - -function GroupAIStateBesiege:_update_individual_group_spawning(id) - - local spawn_task = self._spawning_groups[id] - if not spawn_task then - return - end - local nr_units_spawned = 0 - local produce_data = { - name = true, - spawn_ai = {} - } - local group_ai_tweak = tweak_data.group_ai - local spawn_points = spawn_task.spawn_group.spawn_pts - local function _try_spawn_unit(u_type_name, spawn_entry) - if nr_units_spawned >= GroupAIStateBesiege._MAX_SIMULTANEOUS_SPAWNS then - return - end - local hopeless = true - for _, sp_data in ipairs(spawn_points) do - local category = group_ai_tweak.unit_categories[u_type_name] - if (sp_data.accessibility == "any" or category.access[sp_data.accessibility]) and (not sp_data.amount or sp_data.amount > 0) and sp_data.mission_element:enabled() then - hopeless = false - if self._t > sp_data.delay_t then - produce_data.name = category.units[math.random(#category.units)] - local spawned_unit = sp_data.mission_element:produce(produce_data) - local u_key = spawned_unit:key() - local objective - if spawn_task.objective then - objective = self.clone_objective(spawn_task.objective) - else - objective = spawn_task.group.objective.element:get_random_SO(spawned_unit) - if not objective then - spawned_unit:set_slot(0) - return true - end - objective.grp_objective = spawn_task.group.objective - end - local u_data = self._police[u_key] - self:set_enemy_assigned(objective.area, u_key) - if spawn_entry.tactics then - u_data.tactics = spawn_entry.tactics - u_data.tactics_map = {} - for _, tactic_name in ipairs(u_data.tactics) do - u_data.tactics_map[tactic_name] = true - end - end - spawned_unit:brain():set_spawn_entry(spawn_entry, u_data.tactics_map) - u_data.rank = spawn_entry.rank - self:_add_group_member(spawn_task.group, u_key) - if spawned_unit:brain():is_available_for_assignment(objective) then - if objective.element then - objective.element:clbk_objective_administered(spawned_unit) - end - spawned_unit:brain():set_objective(objective) - else - spawned_unit:brain():set_followup_objective(objective) - end - nr_units_spawned = nr_units_spawned + 1 - if spawn_task.ai_task then - spawn_task.ai_task.force_spawned = spawn_task.ai_task.force_spawned + 1 - end - sp_data.delay_t = self._t + sp_data.interval - if sp_data.amount then - sp_data.amount = sp_data.amount - 1 - end - return true - end - end - end - if hopeless then - debug_pause("[GroupAIStateBesiege:_upd_group_spawning] spawn group", spawn_task.spawn_group.id, "failed to spawn unit", u_type_name) - return true - end - end - - for u_type_name, spawn_info in pairs(spawn_task.units_remaining) do - if not group_ai_tweak.unit_categories[u_type_name].access.acrobatic then - for i = spawn_info.amount, 1, -1 do - local success = _try_spawn_unit(u_type_name, spawn_info.spawn_entry) - if success then - spawn_info.amount = spawn_info.amount - 1 - end - end - end - end - for u_type_name, spawn_info in pairs(spawn_task.units_remaining) do - for i = spawn_info.amount, 1, -1 do - local success = _try_spawn_unit(u_type_name, spawn_info.spawn_entry) - if success then - spawn_info.amount = spawn_info.amount - 1 - end - end - end - local complete = true - for u_type_name, spawn_info in pairs(spawn_task.units_remaining) do - if 0 < spawn_info.amount then - complete = false - else - end - end - if complete then - Print("Finished spawning group: ", tostring( spawn_task.group.id )) - spawn_task.group.has_spawned = true - table.remove(self._spawning_groups, 1) - if 0 >= spawn_task.group.size then - self._groups[spawn_task.group.id] = nil - end - end -end - -function GroupAIStateBesiege:_upd_recon_tasks() - local task_data = self._task_data.recon.tasks[1] - self:_assign_enemy_groups_to_recon() - if not task_data then - return - end - local t = self._t - self:_assign_assault_groups_to_retire() - local target_pos = task_data.target_area.pos - local nr_wanted = self:_get_difficulty_dependent_value(tweak_data.group_ai.besiege.recon.force) - self:_count_police_force("recon") - if nr_wanted <= 0 then - return - end - local used_event, used_spawn_points, reassigned - if task_data.use_spawn_event then - task_data.use_spawn_event = false - if self:_try_use_task_spawn_event(t, task_data.target_area, "recon") then - used_event = true - end - end - - local spawn_group, spawn_group_type = self:_find_spawn_group_near_area(task_data.target_area, tweak_data.group_ai.besiege.recon.groups, nil, nil, callback(self, self, "_verify_anticipation_spawn_point")) - if spawn_group then - local grp_objective = { - type = "recon_area", - area = spawn_group.area, - target_area = task_data.target_area, - attitude = "engage", - stance = "hos", - moving_in = true, - charge = true, - open_fire = true, - } - self:_spawn_in_group(spawn_group, spawn_group_type, grp_objective) - end - - if used_event or used_spawn_points or reassigned then - table.remove(self._task_data.recon.tasks, 1) - self._task_data.recon.next_dispatch_t = t + math.ceil(self:_get_difficulty_dependent_value(tweak_data.group_ai.besiege.recon.interval)) + math.random() * tweak_data.group_ai.besiege.recon.interval_variation - end -end - -function GroupAIStateBesiege:_upd_reenforce_tasks() - local reenforce_tasks = self._task_data.reenforce.tasks - local t = self._t - local i = #reenforce_tasks - while i > 0 do - local task_data = reenforce_tasks[i] - local force_settings = task_data.target_area.factors.force - local force_required = force_settings and force_settings.force - if force_required then - local force_occupied = 0 - for group_id, group in pairs(self._groups) do - if (group.objective.target_area or group.objective.area) == task_data.target_area and group.objective.type == "reenforce_area" then - force_occupied = force_occupied + (group.has_spawned and group.size or group.initial_size) - end - end - local undershot = force_required - force_occupied - if undershot > 0 and not self._task_data.regroup.active and self._task_data.assault.phase ~= "fade" and t > self._task_data.reenforce.next_dispatch_t and self:is_area_safe(task_data.target_area) then - local used_event - if task_data.use_spawn_event then - task_data.use_spawn_event = false - if self:_try_use_task_spawn_event(t, task_data.target_area, "reenforce") then - used_event = true - end - end - local used_group, spawning_groups - - local spawn_group, spawn_group_type = self:_find_spawn_group_near_area(task_data.target_area, tweak_data.group_ai.besiege.reenforce.groups, nil, nil, nil) - if spawn_group then - local grp_objective = { - type = "reenforce_area", - area = spawn_group.area, - target_area = task_data.target_area, - attitude = "engage", - stance = "hos", - pose = "stand", - moving_in = true, - charge = true, - open_fire = true, - } - self:_spawn_in_group(spawn_group, spawn_group_type, grp_objective) - used_group = true - end - - elseif undershot < 0 then - local force_defending = 0 - for group_id, group in pairs(self._groups) do - if group.objective.area == task_data.target_area and group.objective.type == "reenforce_area" then - force_defending = force_defending + (group.has_spawned and group.size or group.initial_size) - end - end - local overshot = force_defending - force_required - if overshot > 0 then - local closest_group, closest_group_size - for group_id, group in pairs(self._groups) do - if group.has_spawned then - if (group.objective.target_area or group.objective.area) == task_data.target_area and group.objective.type == "reenforce_area" and (not closest_group_size or closest_group_size < group.size) and overshot >= group.size then - closest_group = group - closest_group_size = group.size - end - end - end - if closest_group then - self:_assign_group_to_retire(closest_group) - end - end - end - else - for group_id, group in pairs(self._groups) do - if group.has_spawned then - if (group.objective.target_area or group.objective.area) == task_data.target_area and group.objective.type == "reenforce_area" then - self:_assign_group_to_retire(group) - end - end - end - reenforce_tasks[i] = reenforce_tasks[#reenforce_tasks] - table.remove(reenforce_tasks) - end - i = i - 1 - end - self:_assign_enemy_groups_to_reenforce() -end - -function GroupAIStateBesiege:_upd_assault_task() - local task_data = self._task_data.assault - if not task_data.active then - return - end - local t = self._t - self:_assign_recon_groups_to_retire() - local force_pool = self:_get_difficulty_dependent_value(tweak_data.group_ai.besiege.assault.force_pool) * self:_get_balancing_multiplier(tweak_data.group_ai.besiege.assault.force_pool_balance_mul) - local task_spawn_allowance = force_pool - (self._hunt_mode and 0 or task_data.force_spawned) - if task_data.phase == "anticipation" then - if task_spawn_allowance <= 0 then - task_data.phase = "fade" - task_data.phase_end_t = t + tweak_data.group_ai.besiege.assault.fade_duration - elseif t > task_data.phase_end_t or self._drama_data.zone == "high" then - managers.mission:call_global_event("start_assault") - managers.hud:start_assault() - self:_set_rescue_state(false) - task_data.phase = "build" - task_data.phase_end_t = self._t + tweak_data.group_ai.besiege.assault.build_duration - task_data.is_hesitating = nil - self:set_assault_mode(true) - managers.trade:set_trade_countdown(false) - else - managers.hud:check_anticipation_voice(task_data.phase_end_t - t) - managers.hud:check_start_anticipation_music(task_data.phase_end_t - t) - if task_data.is_hesitating and self._t > task_data.voice_delay then - if 0 < self._hostage_headcount then - local best_group - for _, group in pairs(self._groups) do - if not best_group or group.objective.type == "reenforce_area" then - best_group = group - elseif best_group.objective.type ~= "reenforce_area" and group.objective.type ~= "retire" then - best_group = group - end - end - if best_group and self:_voice_delay_assault(best_group) then - task_data.is_hesitating = nil - end - else - task_data.is_hesitating = nil - end - end - end - elseif task_data.phase == "build" then - if task_spawn_allowance <= 0 then - task_data.phase = "fade" - task_data.phase_end_t = t + tweak_data.group_ai.besiege.assault.fade_duration - elseif t > task_data.phase_end_t or self._drama_data.zone == "high" then - task_data.phase = "sustain" - task_data.phase_end_t = t + math.lerp(self:_get_difficulty_dependent_value(tweak_data.group_ai.besiege.assault.sustain_duration_min), self:_get_difficulty_dependent_value(tweak_data.group_ai.besiege.assault.sustain_duration_max), math.random()) * self:_get_balancing_multiplier(tweak_data.group_ai.besiege.assault.sustain_duration_balance_mul) - end - elseif task_data.phase == "sustain" then - if task_spawn_allowance <= 0 then - task_data.phase = "fade" - task_data.phase_end_t = t + tweak_data.group_ai.besiege.assault.fade_duration - elseif t > task_data.phase_end_t and not self._hunt_mode then - task_data.phase = "fade" - task_data.phase_end_t = t + tweak_data.group_ai.besiege.assault.fade_duration - end - else - local enemies_left = self:_count_police_force("assault") - if enemies_left < 7 or t > task_data.phase_end_t + 350 then - if t > task_data.phase_end_t - 8 and not task_data.said_retreat then - if self._drama_data.amount < tweak_data.drama.assault_fade_end then - task_data.said_retreat = true - self:_police_announce_retreat() - end - elseif t > task_data.phase_end_t and self._drama_data.amount < tweak_data.drama.assault_fade_end and self:_count_criminals_engaged_force(4) <= 3 then - task_data.active = nil - task_data.phase = nil - task_data.said_retreat = nil - if self._draw_drama then - self._draw_drama.assault_hist[#self._draw_drama.assault_hist][2] = t - end - managers.mission:call_global_event("end_assault") - self:_begin_regroup_task() - return - end - else - end - end - if self._drama_data.amount <= tweak_data.drama.low then - for criminal_key, criminal_data in pairs(self._player_criminals) do - self:criminal_spotted(criminal_data.unit) - for group_id, group in pairs(self._groups) do - if group.objective.charge then - for u_key, u_data in pairs(group.units) do - u_data.unit:brain():clbk_group_member_attention_identified(nil, criminal_key) - end - end - end - end - end - local primary_target_area = task_data.target_areas[1] - if self:is_area_safe(primary_target_area) then - local target_pos = primary_target_area.pos - local nearest_area, nearest_dis - for criminal_key, criminal_data in pairs(self._player_criminals) do - if not criminal_data.status then - local dis = mvector3.distance_sq(target_pos, criminal_data.m_pos) - if not nearest_dis or nearest_dis > dis then - nearest_dis = dis - nearest_area = self:get_area_from_nav_seg_id(criminal_data.tracker:nav_segment()) - end - end - end - if nearest_area then - primary_target_area = nearest_area - task_data.target_areas[1] = nearest_area - end - end - local nr_wanted = task_data.force - self:_count_police_force("assault") - if task_data.phase == "anticipation" then - nr_wanted = nr_wanted - 5 - end - if nr_wanted > 0 and task_data.phase ~= "fade" then - local used_event - if task_data.use_spawn_event and task_data.phase ~= "anticipation" then - task_data.use_spawn_event = false - if self:_try_use_task_spawn_event(t, primary_target_area, "assault") then - used_event = true - end - end - - local spawn_group, spawn_group_type = self:_find_spawn_group_near_area(primary_target_area, tweak_data.group_ai.besiege.assault.groups, nil, nil, nil) - if spawn_group then - local grp_objective = { - type = "assault_area", - area = spawn_group.area, - coarse_path = { - { - spawn_group.area.pos_nav_seg, - spawn_group.area.pos - } - }, - attitude = "engage", - pose = "crouch", - stance = "hos", - moving_in = true, - charge = true, - open_fire = true, - } - self:_spawn_in_group(spawn_group, spawn_group_type, grp_objective, task_data) - end - - end - if task_data.phase ~= "anticipation" then - if t > task_data.use_smoke_timer then - task_data.use_smoke = true - end - if self._smoke_grenade_queued and task_data.use_smoke and not self:is_smoke_grenade_active() then - self:detonate_smoke_grenade(self._smoke_grenade_queued[1], self._smoke_grenade_queued[1], self._smoke_grenade_queued[2], self._smoke_grenade_queued[4]) - if self._smoke_grenade_queued[3] then - self._smoke_grenade_ignore_control = true - end - end - end - self:_assign_enemy_groups_to_assault(task_data.phase) -end - -function GroupAIStateBesiege:_spawn_in_group(spawn_group, spawn_group_type, grp_objective, ai_task) - - local spawn_group_desc = tweak_data.group_ai.enemy_spawn_groups[spawn_group_type] - local wanted_nr_units - if type(spawn_group_desc.amount) == "number" then - wanted_nr_units = spawn_group_desc.amount - else - wanted_nr_units = math.random(spawn_group_desc.amount[1], spawn_group_desc.amount[2]) - end - local valid_unit_types = {} - self._extract_group_desc_structure(spawn_group_desc.spawn, valid_unit_types) - local function _get_special_unit_type_count(special_type) - if not self._special_units[special_type] then - return 0 - end - return table.size(self._special_units[special_type]) - end - - local unit_categories = tweak_data.group_ai.unit_categories - local total_wgt = 0 - local i = 1 - while i <= #valid_unit_types do - local spawn_entry = valid_unit_types[i] - local cat_data = unit_categories[spawn_entry.unit] - if not cat_data then - debug_pause("[GroupAIStateBesiege:_spawn_in_group] unit category doesn't exist:", spawn_entry.unit) - return - elseif cat_data.special_type and tweak_data.group_ai.special_unit_spawn_limits[cat_data.special_type] then - if _get_special_unit_type_count(cat_data.special_type) + (spawn_entry.amount_min or 0) > tweak_data.group_ai.special_unit_spawn_limits[cat_data.special_type] then - spawn_group.delay_t = 0 - return - end - else - total_wgt = total_wgt + spawn_entry.freq - i = i + 1 - end - end - local spawn_task = { - objective = not grp_objective.element and self._create_objective_from_group_objective(grp_objective), - units_remaining = {}, - spawn_group = spawn_group, - spawn_group_type = spawn_group_type, - ai_task = ai_task - } - Print("Adding group to spawning queue: ", spawn_group_type) - table.insert(self._spawning_groups, spawn_task) - local function _add_unit_type_to_spawn_task(i, spawn_entry) - local spawn_amount_mine = 1 + (spawn_task.units_remaining[spawn_entry.unit] and spawn_task.units_remaining[spawn_entry.unit].amount or 0) - spawn_task.units_remaining[spawn_entry.unit] = {amount = spawn_amount_mine, spawn_entry = spawn_entry} - wanted_nr_units = wanted_nr_units - 1 - if spawn_entry.amount_min then - spawn_entry.amount_min = spawn_entry.amount_min - 1 - end - if spawn_entry.amount_max then - spawn_entry.amount_max = spawn_entry.amount_max - 1 - if spawn_entry.amount_max == 0 then - table.remove(valid_unit_types, i) - total_wgt = total_wgt - spawn_entry.freq - return true - end - end - end - - local i = 1 - while i <= #valid_unit_types do - local spawn_entry = valid_unit_types[i] - if i <= #valid_unit_types and wanted_nr_units > 0 and spawn_entry.amount_min and 0 < spawn_entry.amount_min and (not spawn_entry.amount_max or 0 < spawn_entry.amount_max) then - if not _add_unit_type_to_spawn_task(i, spawn_entry) then - i = i + 1 - end - else - i = i + 1 - end - end - while wanted_nr_units > 0 and #valid_unit_types ~= 0 do - local rand_wght = math.random() * total_wgt - local rand_i = 1 - local rand_entry - while true do - rand_entry = valid_unit_types[rand_i] - rand_wght = rand_wght - rand_entry.freq - if rand_wght <= 0 then - break - else - rand_i = rand_i + 1 - end - end - local cat_data = unit_categories[rand_entry.unit] - if cat_data.special_type and tweak_data.group_ai.special_unit_spawn_limits[cat_data.special_type] and _get_special_unit_type_count(cat_data.special_type) >= tweak_data.group_ai.special_unit_spawn_limits[cat_data.special_type] then - table.remove(valid_unit_types, rand_i) - total_wgt = total_wgt - rand_entry.freq - else - _add_unit_type_to_spawn_task(rand_i, rand_entry) - end - end - local group_desc = {type = spawn_group_type, size = 0} - for u_name, spawn_info in pairs(spawn_task.units_remaining) do - group_desc.size = group_desc.size + spawn_info.amount - end - local group = self:_create_group(group_desc) - group.objective = grp_objective - group.objective.moving_out = true - group.team = self._teams[spawn_group.team_id or tweak_data.levels:get_default_team_ID("combatant")] - spawn_task.group = group - return group -end -]] diff --git a/GoonMod/mods/disabled/stat_trak.lua b/GoonMod/mods/disabled/stat_trak.lua deleted file mode 100644 index 5c89b65..0000000 --- a/GoonMod/mods/disabled/stat_trak.lua +++ /dev/null @@ -1,372 +0,0 @@ - --- Mod Definition -local Mod = class( BaseMod ) -Mod.id = "StatTrakWeapons" -Mod.Name = "Stat-trak Weapons" -Mod.Desc = "" -Mod.Requirements = {} -Mod.Incompatibilities = {} -Mod.EnabledByDefault = true - -Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod:ID(), function() - GoonBase.Mods:RegisterMod( Mod ) -end) - -if not Mod:IsEnabled() then - return -end - --- Stat-trak -_G.GoonBase.StatTrak = _G.GoonBase.StatTrak or {} -local StatTrak = _G.GoonBase.StatTrak -StatTrak.MenuId = "goonbase_stattrak_menu" -StatTrak.InspectWeaponKey = "StattrakInspectWeapon" -StatTrak.CycleStatKey = "StattrakCycleMode" -StatTrak.CustomKeys = { - INSPECT = GoonBase.Options.StatTrak ~= nil and GoonBase.Options.StatTrak.InspectWeaponKey or "", - CYCLE_MODE = GoonBase.Options.StatTrak ~= nil and GoonBase.Options.StatTrak.CycleStatKey or "" -} - -StatTrak.CurrentMode = 1 -StatTrak.Modes = { - [1] = "all", - [2] = "bulldozers", - [3] = "shields", - [4] = "tasers", - [5] = "snipers", - [6] = "cloakers", -} - -StatTrak.ModesFriendlyText = { - ["all"] = "", - ["bulldozers"] = "Bulldozers", - ["shields"] = "Shields", - ["tasers"] = "Tasers", - ["snipers"] = "Snipers", - ["cloakers"] = "Cloakers", -} - -StatTrak._example_kills = { - ["all"] = 136, - ["bulldozers"] = 1, - ["shields"] = 12, - ["tasers"] = 7, - ["snipers"] = 4, - ["cloakers"] = 0, -} - --- Load Weapon Offsets -StatTrak.WeaponOffsetsFile = "mods/stat_trak_weapon_offsets.lua" -SafeDoFile( GoonBase.Path .. StatTrak.WeaponOffsetsFile ) - --- Options -if GoonBase.Options.StatTrak == nil then - GoonBase.Options.StatTrak = {} - GoonBase.Options.StatTrak.InspectWeaponKey = "" - GoonBase.Options.StatTrak.CycleStatKey = "" -end - --- Menu -Hooks:Add("MenuManagerSetupCustomMenus", "MenuManagerSetupCustomMenus_" .. Mod:ID(), function(menu_manager, menu_nodes) - MenuHelper:NewMenu( StatTrak.MenuId ) -end) - -Hooks:Add("MenuManagerSetupGoonBaseMenu", "MenuManagerSetupGoonBaseMenu_" .. Mod:ID(), function( menu_manager ) - - -- Add corpse mod menu button - MenuHelper:AddButton({ - id = "stattrak_mod_menu_button", - title = "OptionsMenu_StatTrakSubmenuTitle", - desc = "OptionsMenu_StatTrakSubmenuDesc", - next_node = StatTrak.MenuId, - menu_id = "goonbase_options_menu" - }) - - MenuHelper:AddKeybinding({ - id = "keybind_stattrak_inspect", - title = managers.localization:text("OptionsMenu_InspectWeapon"), - desc = managers.localization:text("OptionsMenu_InspectWeaponDesc"), - connection_name = StatTrak.InspectWeaponKey, - button = StatTrak.CustomKeys.INSPECT, - binding = StatTrak.CustomKeys.INSPECT, - menu_id = StatTrak.MenuId, - priority = 20 - }) - - MenuHelper:AddKeybinding({ - id = "keybind_stattrak_cycle", - title = managers.localization:text("OptionsMenu_CycleStattrakMode"), - desc = managers.localization:text("OptionsMenu_CycleStattrakModeDesc"), - connection_name = StatTrak.CycleStatKey, - button = StatTrak.CustomKeys.CYCLE_MODE, - binding = StatTrak.CustomKeys.CYCLE_MODE, - menu_id = StatTrak.MenuId, - priority = 19 - }) - -end) - -Hooks:Add("MenuManagerBuildCustomMenus", "MenuManagerBuildCustomMenus_" .. Mod:ID(), function(menu_manager, mainmenu_nodes) - mainmenu_nodes[StatTrak.MenuId] = MenuHelper:BuildMenu( StatTrak.MenuId ) -end) - --- Custom keybinds -Hooks:Add("CustomizeControllerOnKeySet", "CustomizeControllerOnKeySet_" .. Mod:ID(), function(item) - - local psuccess, perror = pcall(function() - - if item._name == StatTrak.InspectWeaponKey then - StatTrak.CustomKeys.INSPECT = item._input_name_list[1] - StatTrak:SaveBindings() - end - - if item._name == StatTrak.CycleStatKey then - StatTrak.CustomKeys.CYCLE_MODE = item._input_name_list[1] - StatTrak:SaveBindings() - end - - end) - if not psuccess then - Print("[Error] " .. perror) - end - -end) - -function StatTrak:SaveBindings() - GoonBase.Options.StatTrak.InspectWeaponKey = StatTrak.CustomKeys.INSPECT - GoonBase.Options.StatTrak.CycleStatKey = StatTrak.CustomKeys.CYCLE_MODE - GoonBase.Options:Save() -end - -Hooks:Add("GameSetupUpdate", "GameSetupUpdate_" .. Mod:ID(), function(t, dt) - StatTrak:UpdateBindings() -end) - -function StatTrak:UpdateBindings() - - if self._input == nil then - self._input = Input:keyboard() - end - if managers.hud:chat_focus() then - return - end - - local inspect_key = StatTrak.CustomKeys.INSPECT - if not string.is_nil_or_empty(inspect_key) then - if self._input:pressed(Idstring(inspect_key)) then - StatTrak._inspecting = true - local state = managers.player:local_player():movement():current_state() - state:_stance_entered() - end - if self._input:released(Idstring(inspect_key)) then - StatTrak._inspecting = false - local state = managers.player:local_player():movement():current_state() - state:_stance_entered() - end - end - - local cycle_key = StatTrak.CustomKeys.CYCLE_MODE - if not string.is_nil_or_empty(cycle_key) and self._input:pressed(Idstring(cycle_key)) then - StatTrak:CycleMode() - end - -end - -function StatTrak:GetCurrentModeName() - return StatTrak.Modes[StatTrak.CurrentMode] -end - -function StatTrak:CycleMode() - - StatTrak.CurrentMode = StatTrak.CurrentMode + 1 - if StatTrak.CurrentMode > #StatTrak.Modes then - StatTrak.CurrentMode = 1 - end - -end - -Hooks:Add("FPCameraPlayerBaseStanceEnteredCallback", "FPCameraPlayerBaseStanceEnteredCallback_" .. Mod:ID(), function(camera, new_shoulder_stance, new_head_stance, new_vel_overshot, new_fov, new_shakers, stance_mod, duration_multiplier, duration) - - local psuccess, perror = pcall(function() - - if StatTrak._inspecting then - - -- local factory_id = managers.blackmarket:equipped_primary().factory_id - local factory_id = managers.player:local_player():inventory():equipped_unit():base()._factory_id - PrintStr(factory_id) - if factory_id ~= nil then - - local default_offset = StatTrak.StandardOffsets - local weapon_offset = StatTrak.WeaponOffsets[ factory_id ] - - stance_mod.rotation = weapon_offset.inspect_rotation_offset or default_offset.inspect_rotation_offset - stance_mod.translation = weapon_offset.inspect_position_offset or default_offset.inspect_position_offset - - end - - end - - end) - if not psuccess then - Print("[Error] " .. perror) - end - -end) - -function StatTrak.add_kill_tracker(weapon, unit) - - local psuccess, perror = pcall(function() - - local self = weapon - self._unit:set_extension_update_enabled( Idstring("base"), true ) - - local gui_object = unit:get_object( Idstring("a_body") ) - if gui_object == nil then - return - end - - self._gui_object = gui_object - self._ws_params = StatTrak:GetDefaultOffsets() - - local w = self._ws_params.w - local h = self._ws_params.h - local scaled_w, scaled_h = self._ws_params.w * self._ws_params.scale, self._ws_params.h * self._ws_params.scale - - local rot = gui_object:rotation() * self._ws_params.rotation_offset( gui_object ) - local pos = gui_object:position() + self._ws_params.position_offset( gui_object ) - - local new_gui = World:newgui() - local ws = new_gui:create_world_workspace(w, h, pos, Vector3(scaled_w, 0, 0):rotate_with(rot), Vector3(0, 0, -scaled_h):rotate_with(rot)) - self._ws = ws - - ws:panel():clear() - ws:panel():set_alpha(0.8) - ws:panel():rect({ - color = Color.black, - layer = -1 - }) - self._back_drop_gui = MenuBackdropGUI:new(ws) - local panel = self._back_drop_gui:get_new_background_layer() - local default_offset = 112 - local font_size = 220 - self._kills_display = panel:text({ - text = "1234", - y = self._ws:panel():h() / 2 - default_offset, - font = tweak_data.menu.pd2_large_font, - align = "center", - vertical = "center", - font_size = font_size, - layer = 0, - visible = true, - color = OffshoreGui.MONEY_COLOR - }) - - local font_size = 80 - local default_offset = 40 - self._mode_display = panel:text({ - text = "", - y = self._ws:panel():h() / 2 - default_offset, - font = tweak_data.menu.pd2_large_font, - align = "right", - vertical = "center", - font_size = font_size, - layer = 0, - visible = true, - color = TextGui.COLORS.light_red:with_alpha(0.6) - }) - - end) - if not psuccess then - Print("[Error] " .. perror) - end - -end - -Hooks:Add("NewRaycastWeaponBaseInit", "NewRaycastWeaponBaseInit_" .. Mod:ID(), function( weapon, unit ) - - if NewRaycastWeaponBase.add_kill_tracker == nil then - NewRaycastWeaponBase.add_kill_tracker = StatTrak.add_kill_tracker - end - - weapon:add_kill_tracker(unit) - -end) - -function StatTrak.update_kill_tracker_offsets(weapon, factory_id) - weapon._ws_params = StatTrak:GetOffsetsForWeaponID(factory_id) -end - -Hooks:Add("NewRaycastWeaponBaseSetFactoryData", "NewRaycastWeaponBaseSetFactoryData_" .. Mod:ID(), function(weapon, factory_id) - - if NewRaycastWeaponBase.update_kill_tracker_offsets == nil then - NewRaycastWeaponBase.update_kill_tracker_offsets = StatTrak.update_kill_tracker_offsets - end - - weapon:update_kill_tracker_offsets(factory_id) - -end) - -function StatTrak.update_kill_tracker_text(weapon, unit, t, dt) - - if not StatTrak.DefaultKillText then - StatTrak.DefaultKillText = managers.localization:get_default_macro("BTN_SKULL") .. " {0}" - end - - local psuccess, perror = pcall(function() - - if weapon._kills_display then - local kills = StatTrak._example_kills[ StatTrak:GetCurrentModeName() ] or 1024 - weapon._kills_display:set_text( StatTrak.DefaultKillText:gsub("{0}", tostring(kills)) ) - end - if weapon._mode_display then - weapon._mode_display:set_text( StatTrak.ModesFriendlyText[ StatTrak:GetCurrentModeName() ] ) - end - - end) - if not psuccess then - Print("[Error] " .. perror) - end - -end - -function StatTrak.update_kill_tracker_position(weapon, unit, t, dt) - - local psuccess, perror = pcall(function() - - local self = weapon - if self._gui_object and self._ws then - - local ws = self._ws - local gui_object = self._gui_object - - local w = self._ws_params.w - local h = self._ws_params.h - local scaled_w, scaled_h = self._ws_params.w * self._ws_params.scale, self._ws_params.h * self._ws_params.scale - - local pos = gui_object:position() + self._ws_params.position_offset( gui_object ) - local rot = gui_object:rotation() * self._ws_params.rotation_offset( gui_object ) - - ws:set_world( w, h, pos, Vector3(scaled_w, 0, 0):rotate_with(rot), Vector3(0, 0, -scaled_h):rotate_with(rot) ) - - end - - end) - if not psuccess then - Print("[Error] " .. perror) - end - -end - -Hooks:Add("NewRaycastWeaponBaseUpdate", "NewRaycastWeaponBaseUpdate_" .. Mod:ID(), function(weapon, unit, t, dt) - - if NewRaycastWeaponBase.update_kill_tracker_text == nil then - NewRaycastWeaponBase.update_kill_tracker_text = StatTrak.update_kill_tracker_text - end - if NewRaycastWeaponBase.update_kill_tracker_position == nil then - NewRaycastWeaponBase.update_kill_tracker_position = StatTrak.update_kill_tracker_position - end - - weapon:update_kill_tracker_text(unit, t, dt) - weapon:update_kill_tracker_position(unit, t, dt) - -end) diff --git a/GoonMod/mods/disabled/stat_trak_weapon_offsets.lua b/GoonMod/mods/disabled/stat_trak_weapon_offsets.lua deleted file mode 100644 index 502b925..0000000 --- a/GoonMod/mods/disabled/stat_trak_weapon_offsets.lua +++ /dev/null @@ -1,62 +0,0 @@ - -local StatTrak = _G.GoonBase.StatTrak - -function StatTrak:GetDefaultOffsets() - return clone( StatTrak.StandardOffsets ) -end - -function StatTrak:GetOffsetsForWeaponID(factory_id) - - local offsets = self:GetDefaultOffsets() - local weapon_offsets = StatTrak.WeaponOffsets[ factory_id ] - if weapon_offsets == nil then - Print("[Warning] Weapon factory ID '", tostring(factory_id) ,"' does not exist in weapon offsets table. Using default offsets.") - return offsets - end - - offsets.w = weapon_offsets.w or offsets.w - offsets.h = weapon_offsets.h or offsets.h - offsets.scale = weapon_offsets.scale or offsets.scale - offsets.position_offset = weapon_offsets.position_offset or offsets.position_offset - offsets.rotation_offset = weapon_offsets.rotation_offset or offsets.rotation_offset - offsets.inspect_position_offset = weapon_offsets.inspect_position_offset or offsets.inspect_position_offset - offsets.inspect_rotation_offset = weapon_offsets.inspect_rotation_offset or offsets.inspect_rotation_offset - - return offsets - -end - -StatTrak.StandardOffsets = { - w = 960, - h = 240, - scale = 0.01, - position_offset = function(parent) - local rot = parent:rotation() - return (rot:x() * 0) + (rot:y() * 0) + (rot:z() * 0) - end, - rotation_offset = function(parent) - return Rotation(0, 0, 0) - end, - inspect_position_offset = Vector3(40, 10, 0), - inspect_rotation_offset = Rotation(80, 10, 45), -} - -StatTrak.WeaponOffsets = { - - ["wpn_fps_pis_1911"] = { - position_offset = function(parent) - local rot = parent:rotation() - return (rot:x() * -1.5) + (rot:y() * 14) + (rot:z() * 7.5) - end, - rotation_offset = function(parent) - return Rotation(-90, 0, 0) - end, - inspect_position_offset = Vector3(40, 10, 0), - inspect_rotation_offset = Rotation(80, 10, 45), - }, - - ["wpn_fps_snp_r93"] = { - - }, - -} diff --git a/GoonMod/mods/disabled/trading.lua b/GoonMod/mods/disabled/trading.lua deleted file mode 100644 index 4d19b9b..0000000 --- a/GoonMod/mods/disabled/trading.lua +++ /dev/null @@ -1,1497 +0,0 @@ - --- Mod Definition -local Mod = class( BaseMod ) -Mod.id = "Trading" -Mod.Name = "Crime.net Cargo" -Mod.Desc = "Send and receive weapons, weapon mods, masks, and mask parts between your friends and other players" -Mod.Requirements = {} -Mod.Incompatibilities = {} - -Hooks:Add("GoonBaseRegisterMods", "GoonBaseRegisterMutators_" .. Mod.id, function() - GoonBase.Mods:RegisterMod( Mod ) -end) - -if not Mod:IsEnabled() then - return -end - --- Trading -_G.GoonBase.Trading = _G.GoonBase.Trading or {} -local Trading = _G.GoonBase.Trading - -Trading.TradablePeers = Trading.TradablePeers or {} -Trading.BlackMarketGUI = nil -Trading.TradeData = {} -Trading.ActiveTradeWindow = nil -Trading.MenuId = "goonbase_trading_options_menu" - -Trading.BlacklistedMasks = { - ["character_locked"] = true, -} - --- Categories -Trading.Categories = {} -Trading.Categories.PrimaryWeapon = "primaries" -Trading.Categories.SecondaryWeapon = "secondaries" -Trading.Categories.WeaponMod = "weapon_mod" -Trading.Categories.Mask = "masks" -Trading.Categories.MaskColour = "colors" -Trading.Categories.MaskPattern = "textures" -Trading.Categories.MaskMaterial = "materials" - --- Network -Trading.Network = Trading.Network or {} -Trading.Network.MessageType = Trading.Network.MessageType or {} -Trading.Network.MessageType.System = "TradeSystem" -Trading.Network.MessageType.Request = "TradeRequest" -Trading.Network.MessageType.RequestResponse = "TradeRequestResponse" -Trading.Network.MessageType.Cancel = "TradeCancel" -Trading.Network.MessageType.Category = "TradeCategory" -Trading.Network.MessageType.Weapon = "TradeWeapon" -Trading.Network.MessageType.WeaponMods = "TradeWeaponMods" -Trading.Network.MessageType.SingleWeaponMod = "TradeSingleWeaponMod" -Trading.Network.MessageType.Mask = "TradeMask" -Trading.Network.MessageType.MaskColour = "TradeColour" -Trading.Network.MessageType.MaskPattern = "TradePattern" -Trading.Network.MessageType.MaskMaterial = "TradeMaterial" - -Trading.Network.RequestTradability = "RequestTradability" -Trading.Network.TradabilityHandshake = "TradabilityHandshake" - -Trading.Network.TradeAccept = "Accept" -Trading.Network.TradeDecline = "Decline" -Trading.Network.AutoDecline_PrimaryWeaponSlots = "DeclinePrimarySlots" -Trading.Network.AutoDecline_SecondaryWeaponSlots = "DeclineSecondarySlots" -Trading.Network.AutoDecline_MaskSlots = "DeclineMaskSlots" -Trading.Network.AutoDecline_AlreadyTrading = "AlreadyTrading" - --- Options -if not GoonBase.Options.Trading then - GoonBase.Options.Trading = {} - GoonBase.Options.Trading.Enabled = true - GoonBase.Options.Trading.FixedPreferredCharacterMask = false -end - --- DLC Names -Trading.DLCNames = { - infamous = "Infamy", - twitch_pack = "Twitch Pack", - dlc1 = "Armoured Transport", - armored_transport = "Armoured Transport", - gage_pack = "Gage Weapon Pack #01", - gage_pack_lmg = "Gage Weapon Pack #02", - gage_pack_jobs = "Gage Mod Courier", - gage_pack_snp = "Gage Sniper Pack", - gage_pack_assault = "Gage Assault Pack", - big_bank = "The Big Bank Heist", - gage_pack_shotgun = "Gage Shotgun Pack", - season_pass = "Season Pass", - animal = "Animal" -} - --- Options Menu -Hooks:Add("MenuManagerSetupCustomMenus", "MenuManagerSetupCustomMenus_Trading", function(menu_manager, menu_nodes) - MenuHelper:NewMenu( Trading.MenuId ) -end) - -Hooks:Add("MenuManagerSetupGoonBaseMenu", "MenuManagerSetupGoonBaseMenu_Trading", function( menu_manager ) - - MenuHelper:AddButton({ - id = "trading_submenu_options_button", - title = "Trading_OptionsMenuTitle", - desc = "Trading_OptionsMenuMessage", - next_node = Trading.MenuId, - menu_id = "goonbase_options_menu", - }) - - MenuCallbackHandler.toggle_trading_enabled = function(this, item) - GoonBase.Options.Trading.Enabled = item:value() == "on" and true or false - GoonBase.Options:Save() - end - - MenuHelper:AddToggle({ - id = "toggle_trading_enabled", - title = "Trading_OptionsTitle", - desc = "Trading_OptionsMessage", - callback = "toggle_trading_enabled", - value = GoonBase.Options.Trading.Enabled, - menu_id = Trading.MenuId, - }) - -end) - -Hooks:Add("MenuManagerBuildCustomMenus", "MenuManagerBuildCustomMenus_Trading", function(menu_manager, mainmenu_nodes) - mainmenu_nodes[Trading.MenuId] = MenuHelper:BuildMenu( Trading.MenuId ) -end) - --- Blackmarket Menu -Hooks:Add("BlackMarketGUIPostSetup", "BlackMarketGUIPostSetup_InjectTradeButton", function(gui, is_start_page, component_data) - - Hooks:RegisterHook("TradingAttemptTradeWeapon") - gui.trade_weapon_callback = function(self, data) - Hooks:Call("TradingAttemptTradeWeapon", data) - end - - Hooks:RegisterHook("TradingAttemptTradeWeaponMod") - gui.trade_weaponmod_callback = function(self, data) - Hooks:Call("TradingAttemptTradeWeaponMod", data) - end - - Hooks:RegisterHook("TradingAttemptTradeMask") - gui.trade_mask_callback = function(self, data) - Hooks:Call("TradingAttemptTradeMask", data) - end - - Hooks:RegisterHook("TradingAttemptTradeMaskPart") - gui.trade_mask_part_callback = function(self, data) - Hooks:Call("TradingAttemptTradeMaskPart", data) - end - - local w_trade = { - prio = 5, - btn = "BTN_STICK_L", - pc_btn = Idstring("menu_toggle_ready"), - name = "Trading_InventorySendToPlayer", - callback = callback(gui, gui, "trade_weapon_callback") - } - - local m_trade = { - prio = 5, - btn = "BTN_STICK_L", - pc_btn = Idstring("menu_toggle_ready"), - name = "Trading_InventorySendToPlayer", - callback = callback(gui, gui, "trade_mask_callback") - } - - local wm_trade = { - prio = 5, - btn = "BTN_STICK_L", - pc_btn = Idstring("menu_toggle_ready"), - name = "Trading_InventorySendToPlayer", - callback = callback(gui, gui, "trade_weaponmod_callback") - } - - local mp_trade = { - prio = 5, - btn = "BTN_STICK_L", - pc_btn = Idstring("menu_toggle_ready"), - name = "Trading_InventorySendToPlayer", - callback = callback(gui, gui, "trade_mask_part_callback") - } - - local btn_x = 10 - gui._btns["w_trade"] = BlackMarketGuiButtonItem:new(gui._buttons, w_trade, btn_x) - gui._btns["m_trade"] = BlackMarketGuiButtonItem:new(gui._buttons, m_trade, btn_x) - gui._btns["wm_trade"] = BlackMarketGuiButtonItem:new(gui._buttons, wm_trade, btn_x) - gui._btns["mp_trade"] = BlackMarketGuiButtonItem:new(gui._buttons, mp_trade, btn_x) - -end) - -Hooks:Add("MenuUpdate", "MenuUpdate_" .. Mod:ID(), function(t, dt) - - -- This is a mess, but it gets custom keybinds for menu items working - if not managers.menu:is_pc_controller() and managers.menu:get_controller():get_input_pressed("run") then - - local psuccess, perror = pcall(function() - - if not managers.menu_component then return end - - local blackmarket_gui = managers.menu_component._blackmarket_gui - if not blackmarket_gui then return end - if not blackmarket_gui._selected_slot then return end - if not blackmarket_gui._selected_slot._data then return end - - local data = blackmarket_gui._selected_slot._data - if data == nil then return end - local category = data.category - if category == Trading.Categories.PrimaryWeapon or category == Trading.Categories.SecondaryWeapon then - blackmarket_gui:trade_weapon_callback( data ) - end - if category == Trading.Categories.WeaponMod then - blackmarket_gui:trade_weaponmod_callback( data ) - end - if category == Trading.Categories.Mask then - blackmarket_gui:trade_mask_callback( data ) - end - if category == Trading.Categories.MaskColour or category == Trading.Categories.MaskPattern or category == Trading.Categories.MaskMaterial then - blackmarket_gui:trade_mask_part_callback( data ) - end - - end) - if not psuccess then - Print("[Error] " .. perror) - end - - end - -end) - -Hooks:Add("BlackMarketGUIOnPopulateWeaponActionList", "BlackMarketGUIOnPopulateWeaponActionList_" .. Mod:ID(), function(gui, data) - - if GoonBase.Options.Trading.Enabled and not data.last_weapon and not data.equipped and GoonBase.Network:IsMultiplayer() then - table.insert(data, "w_trade") - end - -end) - -Hooks:Add("BlackMarketGUIOnPopulateMasksActionList", "BlackMarketGUIOnPopulateMasksActionList_" .. Mod:ID(), function(gui, data) - - Trading:_FixPreferredCharacterMask() - - if GoonBase.Options.Trading.Enabled and not data.equipped and GoonBase.Network:IsMultiplayer() then - if not Trading.BlacklistedMasks[ data.name ] then - table.insert(data, "m_trade") - end - end - -end) - -Hooks:Add("BlackMarketGUIOnPopulateModsActionList", "BlackMarketGUIOnPopulateModsActionList_" .. Mod:ID(), function(gui, data) - - if GoonBase.Options.Trading.Enabled and GoonBase.Network:IsMultiplayer() then - if type(data.unlocked) ~= "number" or data.unlocked > 0 then - table.insert(data, "wm_trade") - end - end - -end) - -Hooks:Add("BlackMarketGUIOnPopulateMaskModsActionList", "BlackMarketGUIOnPopulateMaskModsActionList_" .. Mod:ID(), function(gui, data) - - if GoonBase.Options.Trading.Enabled and not data.equipped and data.amount ~= nil and data.amount > 0 and GoonBase.Network:IsMultiplayer() then - table.insert(data, "mp_trade") - end - -end) - --- Request client tradability when we open the inventory -Hooks:Add("BlackMarketGUIOnPopulateWeapons", "BlackMarketGUIOnPopulateWeapons_Trading", function(gui, category, data) - - if not GoonBase.Options.Trading.Enabled then - return - end - - Trading.BlackMarketGUI = gui - - -- Request tradability from peers - if GoonBase.Network:IsMultiplayer() then - Trading.TradablePeers = {} - GoonBase.Network:SendToPeers(Trading.Network.MessageType.System, Trading.Network.RequestTradability) - end - -end) - -Hooks:Add("BlackMarketGUIOnPopulateMasks", "BlackMarketGUIOnPopulateMasks_Trading", function(gui, category, data) - - if not GoonBase.Options.Trading.Enabled then - return - end - - Trading.BlackMarketGUI = gui - -end) - -Hooks:Add("BlackMarketGUIOnPopulateMods", "BlackMarketGUIOnPopulateMods_Trading", function(gui, category, data) - - if not GoonBase.Options.Trading.Enabled then - return - end - - Trading.BlackMarketGUI = gui - -end) - -Hooks:Add("BlackMarketGUIOnPopulateMaskMods", "BlackMarketGUIOnPopulateMaskMods_Trading", function(gui, category, data) - - if not GoonBase.Options.Trading.Enabled then - return - end - - Trading.BlackMarketGUI = gui - -end) - --- Check if we have any tradable peers -function Trading:HasTradablePeers() - - local tradable_peers = 0 - for k, v in pairs( managers.network:session():peers() ) do - if Trading.TradablePeers[k] == true then - tradable_peers = tradable_peers + 1 - end - end - - if tradable_peers < 1 then - return false - end - - return true - -end - --- Process trade messages -Hooks:Add("NetworkReceivedData", "NetworkReceivedData_Trading", function(sender, messageType, data) - - -- Don't process if disabled - if not GoonBase.Options.Trading.Enabled then - return - end - - -- Trade System Messages - if messageType == Trading.Network.MessageType.System then - - if data == Trading.Network.RequestTradability then - Trading:RequestTradabilityHandshake(sender) - return - end - if data == Trading.Network.TradabilityHandshake then - Trading:CompleteTradabilityHandshake(sender) - return - end - - end - - -- Trade Response - if messageType == Trading.Network.MessageType.RequestResponse then - Trading:TradeResponseReceived(sender, data) - return - end - - -- Trade Already-In-Progress Reply - if Trading.TradeData.CurrentlyTradingWith ~= nil and Trading.TradeData.CurrentlyTradingWith ~= sender then - GoonBase.Network:SendToPeer(sender, Trading.Network.MessageType.RequestResponse, Trading.Network.AutoDecline_AlreadyTrading) - return - end - - -- Make sure player only ever gets trade data from one player - Trading.TradeData.CurrentlyTradingWith = sender - - -- Trade Requests - if messageType == Trading.Network.MessageType.Request then - Trading:TradeRequested(sender) - return - end - - -- Trade Cancel - if messageType == Trading.Network.MessageType.Cancel then - Trading:HandleTradeCancelled(sender) - return - end - - -- Trade Data - if messageType == Trading.Network.MessageType.Category then - Trading.TradeData.Category = data - return - end - - if messageType == Trading.Network.MessageType.Weapon then - Trading.TradeData.Weapon = data - return - end - - if messageType == Trading.Network.MessageType.WeaponMods then - if Trading.TradeData.WeaponMods == nil then - Trading.TradeData.WeaponMods = {} - end - table.insert( Trading.TradeData.WeaponMods, data ) - return - end - - if messageType == Trading.Network.MessageType.SingleWeaponMod then - Trading.TradeData.WeaponMod = data - return - end - - -- Mask Data - if messageType == Trading.Network.MessageType.Mask then - Trading.TradeData.Mask = data - end - - -- Mask Mod Data - if messageType == Trading.Network.MessageType.MaskPattern then - if Trading.TradeData.MaskMods == nil then - Trading.TradeData.MaskMods = {} - end - Trading.TradeData.MaskMods.Pattern = data - return - end - - if messageType == Trading.Network.MessageType.MaskMaterial then - if Trading.TradeData.MaskMods == nil then - Trading.TradeData.MaskMods = {} - end - Trading.TradeData.MaskMods.Material = data - return - end - - if messageType == Trading.Network.MessageType.MaskColour then - if Trading.TradeData.MaskMods == nil then - Trading.TradeData.MaskMods = {} - end - Trading.TradeData.MaskMods.Colour = data - return - end - -end) - --- Handle tradability handshake -function Trading:RequestTradabilityHandshake(sender) - if GoonBase.Options.Trading.Enabled then - GoonBase.Network:SendToPeer(sender, Trading.Network.MessageType.System, Trading.Network.TradabilityHandshake) - end -end -function Trading:CompleteTradabilityHandshake(sender) - Trading.TradablePeers[sender] = true -end - --- Handle trade request -function Trading:TradeRequested(sender, data) - - Trading.TradeData.Trader = sender - local category = Trading.TradeData.Category - - -- Check if player has enough primary weapon slots to trade - if category == Trading.Categories.PrimaryWeapon then - - local slot = managers.blackmarket:_get_free_weapon_slot( Trading.Categories.PrimaryWeapon ) - local weaponName = managers.weapon_factory:get_weapon_name_by_weapon_id( Trading.TradeData.Weapon ) - if slot ~= nil then - - Trading:ShowTradeResponseWindow(sender, { name = weaponName, category = "Primary Weapon" }) - else - Trading:ShowFullInventoryWindow(sender, { item = weaponName, slot = "primary weapon" }) - GoonBase.Network:SendToPeer(sender, Trading.Network.MessageType.RequestResponse, Trading.Network.AutoDecline_PrimaryWeaponSlots) - end - - end - - -- Check if player has enough secondary weapon slots to trade - if category == Trading.Categories.SecondaryWeapon then - - local slot = managers.blackmarket:_get_free_weapon_slot( Trading.Categories.SecondaryWeapon ) - local weaponName = managers.weapon_factory:get_weapon_name_by_weapon_id( Trading.TradeData.Weapon ) - if slot ~= nil then - Trading:ShowTradeResponseWindow(sender, { name = weaponName, category = "Secondary Weapon" }) - else - Trading:ShowFullInventoryWindow(sender, { item = weaponName, slot = "secondary weapon" }) - GoonBase.Network:SendToPeer(sender, Trading.Network.MessageType.RequestResponse, Trading.Network.AutoDecline_SecondaryWeaponSlots) - end - - end - - -- Single Weapon Mod Trade - if category == Trading.Categories.WeaponMod then - local modName = managers.weapon_factory:get_part_name_by_part_id( Trading.TradeData.WeaponMod ) - Trading:ShowTradeResponseWindow(sender, { name = modName, category = "Weapon Mod" }) - end - - -- Mask Trading - local success, err = pcall(function() - - if category == Trading.Categories.Mask then - - local mask_name = managers.blackmarket:get_mask_name( Trading.TradeData.Mask ) - - -- Check if player is trying to send premodded mask - if Trading.TradeData.MaskMods ~= nil then - - -- Check if player has free mask slots - local slot = managers.blackmarket:get_free_mask_slot() - if slot ~= nil then - Trading:ShowTradeResponseWindow(sender, { name = mask_name or "error: mask_name", category = "Mask" }) - else - Trading:ShowFullInventoryWindow(sender, { item = mask_name or "error: mask_name", slot = "mask" }) - GoonBase.Network:SendToPeer(sender, Trading.Network.MessageType.RequestResponse, Trading.Network.AutoDecline_MaskSlots) - end - - else - Trading:ShowTradeResponseWindow(sender, { name = mask_name or "error: mask_name", category = "Mask" }) - end - - end - - -- Mask Material - if category == Trading.Categories.MaskMaterial then - local mod_name = managers.blackmarket:get_mask_mod_name( Trading.TradeData.MaskMods.Material, Trading.Categories.MaskMaterial ) - Trading:ShowTradeResponseWindow(sender, { name = mod_name or "error: mod_name", category = "Mask Material" }) - end - - -- Mask Pattern - if category == Trading.Categories.MaskPattern then - local mod_name = managers.blackmarket:get_mask_mod_name( Trading.TradeData.MaskMods.Pattern, Trading.Categories.MaskPattern ) - Trading:ShowTradeResponseWindow(sender, { name = mod_name or "error: mod_name", category = "Mask Pattern" }) - end - - -- Mask Color - if category == Trading.Categories.MaskColour then - local mod_name = managers.blackmarket:get_mask_mod_name( Trading.TradeData.MaskMods.Colour, Trading.Categories.MaskColour ) - Trading:ShowTradeResponseWindow(sender, { name = mod_name or "error: mod_name", category = "Mask Colours" }) - end - - end) - if not success then Print(err) end - -end - -function Trading:ShowTradeResponseWindow(sender, data) - - local senderName = GoonBase.Network:GetNameFromPeerID(sender) - - local title = managers.localization:text("Trading_TradeWindowTitle") - local message = managers.localization:text("Trading_TradeRequestMessage") - message = message:gsub("{1}", senderName) - message = message:gsub("{2}", data.name) - message = message:gsub("{3}", data.category) - local menuOptions = {} - menuOptions[1] = { - text = managers.localization:text("Trading_TradeRequestAccept"), - callback = Trading.TradeRequestAccept, - is_cancel_button = true - } - menuOptions[2] = { - text = managers.localization:text("Trading_TradeRequestDecline"), - callback = Trading.TradeRequestDecline, - is_cancel_button = true - } - Trading.ActiveTradeWindow = QuickMenu:new(title, message, menuOptions, true) - -end - -function Trading:ShowFullInventoryWindow(sender, data) - - local senderName = GoonBase.Network:GetNameFromPeerID(sender) - - local title = managers.localization:text("Trading_TradeWindowTitle") - local message = managers.localization:text("Trading_InventoryFull") - message = message:gsub("{1}", senderName) - message = message:gsub("{2}", data.item) - message = message:gsub("{3}", data.slot) - local menuOptions = {} - menuOptions[1] = { - text = managers.localization:text("Trading_TradeWindowOk"), - is_cancel_button = true - } - local tradeMenu = QuickMenu:new(title, message, menuOptions, true) - -end - -function Trading.TradeRequestAccept(data) - - GoonBase.Network:SendToPeer(Trading.TradeData.Trader, Trading.Network.MessageType.RequestResponse, Trading.Network.TradeAccept) - - local category = Trading.TradeData.Category - - if category == Trading.Categories.PrimaryWeapon then - Trading:AddWeaponToInventory( Trading.TradeData.Weapon, Trading.TradeData.WeaponMods ) - managers.menu:back(true) - end - - if category == Trading.Categories.SecondaryWeapon then - Trading:AddWeaponToInventory( Trading.TradeData.Weapon, Trading.TradeData.WeaponMods ) - managers.menu:back(true) - end - - if category == Trading.Categories.WeaponMod then - Trading:AddTradedModToInventory() - managers.menu:back(true) - managers.menu:back(true) - end - - if category == Trading.Categories.Mask then - Trading:AddTradedMaskToInventory() - managers.menu:back(true) - end - - if category == Trading.Categories.MaskMaterial or category == Trading.Categories.MaskPattern or category == Trading.Categories.MaskColour then - Trading:AddTradedMaskModToInventory() - end - - Trading.TradeData = {} - -end - -function Trading.TradeRequestDecline(data) - GoonBase.Network:SendToPeer(Trading.TradeData.Trader, Trading.Network.MessageType.RequestResponse, Trading.Network.TradeDecline) - Trading.TradeData = {} -end - --- Handle Trade Response -function Trading:TradeResponseReceived(sender, data) - - if data == Trading.Network.TradeAccept then - Trading:TradeResponseAccept(sender) - return - end - - if data == Trading.Network.AutoDecline_PrimaryWeaponSlots then - Trading:TradeResponseFullInventory("primary weapon") - return - end - - if data == Trading.Network.AutoDecline_SecondaryWeaponSlots then - Trading:TradeResponseFullInventory("secondary weapon") - return - end - - if data == Trading.Network.AutoDecline_MaskSlots then - Trading:TradeResponseFullInventory("mask") - return - end - - if data == Trading.Network.AutoDecline_AlreadyTrading then - Trading:TradeResponseAlreadyTrading() - return - end - - Trading:TradeResponseDecline(sender) - -end - -function Trading:TradeResponseAccept(sender) - - local success, err = pcall(function() - - local category = Trading.TradeData.Category - - if category == Trading.Categories.PrimaryWeapon or category == Trading.Categories.SecondaryWeapon then - Trading:ShowTradeResponseAcceptWindow( sender, Trading.TradeData.RawData.slot.name_localized ) - Trading:RemoveWeaponFromInventory() - managers.menu:back(true) - end - - if category == Trading.Categories.WeaponMod then - Trading:ShowTradeResponseAcceptWindow( sender, Trading.TradeData.RawData.slot.name_localized ) - Trading:RemoveModFromInventory() - managers.menu:back(true) - managers.menu:back(true) - end - - if category == Trading.Categories.Mask then - Trading:ShowTradeResponseAcceptWindow( sender, managers.blackmarket:get_mask_name( Trading.TradeData.RawData.slot.mask_id ) ) - managers.blackmarket:remove_mask_from_inventory( Trading.TradeData.RawData.slot ) - managers.menu:back(true) - end - - if category == Trading.Categories.MaskMaterial or category == Trading.Categories.MaskPattern or category == Trading.Categories.MaskColour then - Trading:ShowTradeResponseAcceptWindow( sender, managers.blackmarket:get_mask_mod_name( Trading.TradeData.RawData.slot.name, Trading.TradeData.RawData.slot.category ) ) - -- TODO - managers.blackmarket:remove_mask_mod_from_inventory( Trading.TradeData.RawData.slot.name, Trading.TradeData.RawData.slot.category ) - end - - end) - - if not success then Print(err) end - -end - -function Trading:ShowTradeResponseAcceptWindow(sender, item_name) - - Trading:CloseActiveWindow() - - local title = managers.localization:text("Trading_TradeWindowTitle") - local message = managers.localization:text("Trading_TradeAccepted") - message = message:gsub("{1}", item_name) - message = message:gsub("{2}", GoonBase.Network:GetNameFromPeerID(Trading.TradeData.Recipient)) - local menuOptions = {} - menuOptions[1] = { text = managers.localization:text("Trading_TradeWindowOk"), is_cancel_button = true } - local tradeMenu = QuickMenu:new(title, message, menuOptions, true) - -end - -function Trading:TradeResponseDecline(sender) - - local success, err = pcall(function() - - Trading:CloseActiveWindow() - - if Trading.TradeData.Recipient == nil then - return - end - - local category = Trading.TradeData.Category - local item_name = "trade" - if category == Trading.Categories.PrimaryWeapon or category == Trading.Categories.SecondaryWeapon or category == Trading.Categories.WeaponMod then - item_name = Trading.TradeData.RawData.slot.name_localized or item_name - end - if category == Trading.Categories.Mask then - item_name = managers.blackmarket:get_mask_name( Trading.TradeData.RawData.slot.mask_id ) - end - - local title = managers.localization:text("Trading_TradeWindowTitle") - local message = managers.localization:text("Trading_TradeDeclined") - message = message:gsub("{1}", GoonBase.Network:GetNameFromPeerID(Trading.TradeData.Recipient)) - message = message:gsub("{2}", item_name) - local menuOptions = {} - menuOptions[1] = { text = managers.localization:text("Trading_TradeWindowOk"), is_cancel_button = true } - local tradeMenu = QuickMenu:new(title, message, menuOptions, true) - - Trading.TradeData = {} - - end) - if not success then Print(err) end - -end - -function Trading:TradeResponseAlreadyTrading() - - local success, err = pcall(function() - - Trading:CloseActiveWindow() - - if Trading.TradeData.Recipient == nil then - return - end - - local title = managers.localization:text("Trading_AlreadyTradingTitle") - local message = managers.localization:text("Trading_AlreadyTradingMessage") - message = message:gsub("{1}", GoonBase.Network:GetNameFromPeerID(Trading.TradeData.Recipient)) - local menuOptions = {} - menuOptions[1] = { text = managers.localization:text("Trading_AlreadyTradingAccept"), is_cancel_button = true } - local tradeMenu = QuickMenu:new(title, message, menuOptions, true) - - Trading.TradeData = {} - - end) - if not success then Print(err) end - -end - -function Trading:TradeResponseFullInventory(reason) - - Trading:CloseActiveWindow() - - local title = managers.localization:text("Trading_TradeWindowTitle") - local message = managers.localization:text("Trading_InventoryFullReason") - message = message:gsub("{1}", managers.weapon_factory:get_weapon_name_by_weapon_id(Trading.TradeData.Weapon)) - message = message:gsub("{2}", GoonBase.Network:GetNameFromPeerID(Trading.TradeData.Recipient)) - message = message:gsub("{3}", reason) - local menuOptions = {} - menuOptions[1] = { text = managers.localization:text("Trading_TradeWindowOk"), is_cancel_button = true } - local tradeMenu = QuickMenu:new(title, message, menuOptions, true) - -end - --- Show Trade Menu when player clicks the trade button -Hooks:Add("TradingAttemptTradeWeapon", "TradingAttemptTradeWeapon_ShowMenu", function(weapon) - Trading:ShowSendToPlayerMenu(weapon.name_localized, weapon) -end) - -Hooks:Add("TradingAttemptTradeWeaponMod", "TradingAttemptTradeWeaponMod_ShowMenu", function(mod) - -- Mods use 'primaries' and 'secondaries' which we use for weapons - -- So we use a custom category to make the trading work with them - mod.category = Trading.Categories.WeaponMod - Trading:ShowSendToPlayerMenu(mod.name_localized, mod) -end) - -Hooks:Add("TradingAttemptTradeMask", "TradingAttemptTradeMask_ShowMenu", function(mask) - - local data = managers.blackmarket:get_mask_slot_data(mask) - if data == nil then - return - end - data.category = "masks" - data.slot = mask.slot - - -- Verify mask parts before sending the mask - -- Some mods get returned to the inventory when selling the mask, stop masks with these on from being traded - -- so that we don't duplicate mods. TODO: Ask Overkill about this before release! - if Trading:VerifyMaskTradability(data) then - Trading:ShowSendToPlayerMenu(mask.name_localized, data) - end - -end) - -Hooks:Add("TradingAttemptTradeMaskPart", "TradingAttemptTradeMaskPart_ShowMenu", function(mod) - - -- Verify mask mod before sending the mask - if Trading:VerifyMaskModTradability(mod) then - Trading:ShowSendToPlayerMenu(mod.name_localized, mod) - end - -end) - -function Trading:ShowSendToPlayerMenu(item_name, item_data) - - if GoonBase.Network:IsMultiplayer() then - - -- Check for tradable peers - if not Trading:HasTradablePeers() then - Trading:NoTradablePeersWindow() - return - end - - -- Show tradable peers - local title = managers.localization:text("Trading_TradeWindowTitle") - local message = managers.localization:text("Trading_TradeWindowMessage"):gsub("{1}", item_name) - local menuOptions = {} - - for k, v in pairs( managers.network:session():peers() ) do - - if Trading.TradablePeers[k] == true then - - local plyData = { - text = v._name, - callback = Trading.SendToPlayerCallback, - data = { - peer = k, - slot = item_data - } - } - table.insert( menuOptions, plyData ) - - end - - end - table.insert( menuOptions, { text = managers.localization:text("Trading_TradeWindowCancel"), is_cancel_button = true } ) - - local tradeMenu = QuickMenu:new(title, message, menuOptions, true) - - else - Trading:ShowOnlineOnlyWindow() - end - -end - -function Trading.SendToPlayerCallback(data) - - local success, err = pcall(function() - - local category = data.slot.category - Trading.TradeData = { - Recipient = data.peer, - Category = category, - RawData = data - } - - -- Send category to recepient - GoonBase.Network:SendToPeer(data.peer, Trading.Network.MessageType.Category, category) - - -- Weapon trading - if category == Trading.Categories.PrimaryWeapon or category == Trading.Categories.SecondaryWeapon then - - -- Ask if we should send just the weapon or with the mods - local title = managers.localization:text("Trading_TradeWindowTitle") - local message = managers.localization:text("Trading_WeaponSendWithMods") - local menuOptions = {} - menuOptions[1] = { - text = managers.localization:text("Trading_WeaponSendMods"), - callback = Trading.CallbackSendWeaponToPlayer, - data = { - trade_data = data, - send_mods = true - } - } - menuOptions[2] = { - text = managers.localization:text("Trading_WeaponDontSendMods"), - callback = Trading.CallbackSendWeaponToPlayer, - data = { - trade_data = data, - send_mods = false - } - } - local tradeMenu = QuickMenu:new(title, message, menuOptions, true) - - end - - -- Weapon Mod Trading - if category == Trading.Categories.WeaponMod then - Trading:CallbackSendModToPlayer(data) - end - - -- Mask Trading - if category == Trading.Categories.Mask then - Trading:CallbackSendMaskToPlayer(data) - end - - -- Mask Material/Pattern/Colour Trading - if category == Trading.Categories.MaskMaterial or category == Trading.Categories.MaskPattern or category == Trading.Categories.MaskColour then - Trading:CallbackSendMaskModToPlayer(data) - end - - end) - if not success then Print(err) end - -end - -function Trading.CallbackSendWeaponToPlayer(data) - - local trade_data = data.trade_data - Trading.TradeData.SendWeaponMods = data.send_mods - - local weaponMods = BlackMarketManager:get_mods_on_weapon(trade_data.slot.category, trade_data.slot.slot) - GoonBase.Network:SendToPeer(trade_data.peer, Trading.Network.MessageType.Weapon, trade_data.slot.name) - - if data.send_mods then - for i, modName in pairs(weaponMods) do - GoonBase.Network:SendToPeer(trade_data.peer, Trading.Network.MessageType.WeaponMods, modName) - end - end - - GoonBase.Network:SendToPeer(trade_data.peer, Trading.Network.MessageType.Request, "") - Trading.TradeData.Weapon = trade_data.slot.name - Trading.TradeData.WeaponMods = weaponMods - Trading.TradeData.Slot = trade_data.slot - - -- Show waiting window - Trading:ShowWaitingResponseWindow() - -end - -function Trading:CallbackSendModToPlayer(data) - - -- Send trade data to player - GoonBase.Network:SendToPeer(data.peer, Trading.Network.MessageType.SingleWeaponMod, data.slot.name) - - -- Send request to player - GoonBase.Network:SendToPeer(data.peer, Trading.Network.MessageType.Request, "") - - -- Show waiting window - Trading:ShowWaitingResponseWindow() - -end - -function Trading:CallbackSendMaskToPlayer(data) - - -- Table stuff - local peer = data.peer - data = data.slot - - -- Send modded mask data to player - if data.modded then - GoonBase.Network:SendToPeer(peer, Trading.Network.MessageType.MaskMaterial, data.blueprint.material.id) - GoonBase.Network:SendToPeer(peer, Trading.Network.MessageType.MaskPattern, data.blueprint.pattern.id) - GoonBase.Network:SendToPeer(peer, Trading.Network.MessageType.MaskColour, data.blueprint.color.id) - end - - -- Send mask data to player - GoonBase.Network:SendToPeer(peer, Trading.Network.MessageType.Mask, data.mask_id) - - -- Send request to player - GoonBase.Network:SendToPeer(peer, Trading.Network.MessageType.Request, "") - - -- Show waiting window - Trading:ShowWaitingResponseWindow() - -end - -function Trading:CallbackSendMaskModToPlayer(data) - - -- Table stuff - local peer = data.peer - data = data.slot - - -- Send data to player - if data.category == Trading.Categories.MaskMaterial then - GoonBase.Network:SendToPeer(peer, Trading.Network.MessageType.MaskMaterial, data.name) - end - if data.category == Trading.Categories.MaskPattern then - GoonBase.Network:SendToPeer(peer, Trading.Network.MessageType.MaskPattern, data.name) - end - if data.category == Trading.Categories.MaskColour then - GoonBase.Network:SendToPeer(peer, Trading.Network.MessageType.MaskColour, data.name) - end - - -- Send request - GoonBase.Network:SendToPeer(peer, Trading.Network.MessageType.Request, "") - - -- Show waiting window - Trading:ShowWaitingResponseWindow() - -end - --- Trading Offline Screen -function Trading:ShowOnlineOnlyWindow() - - local title = managers.localization:text("Trading_OnlineOnlyTitle") - local message = managers.localization:text("Trading_OnlineOnlyMessage") - local menuOptions = {} - menuOptions[1] = { text = managers.localization:text("Trading_OnlineOnlyCancel"), is_cancel_button = true } - local tradeMenu = QuickMenu:new(title, message, menuOptions, true) - -end - --- No Tradable Peers Screen -function Trading:NoTradablePeersWindow() - - local title = managers.localization:text("Trading_NoPeersTitle") - local message = managers.localization:text("Trading_NoPeersMessage") - local menuOptions = {} - menuOptions[1] = { text = managers.localization:text("Trading_NoPeersCancel"), is_cancel_button = true } - local tradeMenu = QuickMenu:new(title, message, menuOptions, true) - -end - --- Trade Wait Screen -function Trading:ShowWaitingResponseWindow() - - local title = managers.localization:text("Trading_WaitingResponseTitle") - local message = managers.localization:text("Trading_WaitingResponseMessage") - message = message:gsub("{1}", GoonBase.Network:GetNameFromPeerID(Trading.TradeData.Recipient)) - local menuOptions = {} - menuOptions[1] = { - text = managers.localization:text("Trading_WaitingResponseCancel"), - callback = Trading.WaitingResponseCancel, - } - Trading.ActiveTradeWindow = QuickMenu:new(title, message, menuOptions) - Trading.ActiveTradeWindow.dialog_data.indicator = true - Trading.ActiveTradeWindow:Show() - -end - --- Trade Cancelling -function Trading.WaitingResponseCancel() - - -- Close trade waiting screen - Trading:CloseActiveWindow() - - -- Tell recipient to clear trade data - GoonBase.Network:SendToPeer(Trading.TradeData.Recipient, Trading.Network.MessageType.Cancel, "") - - -- Clear local trade data - Trading.TradeData = {} - -end - -function Trading:HandleTradeCancelled(sender) - - -- Remove trade response window - Trading:CloseActiveWindow() - - -- Show trade cancelled window - local title = managers.localization:text("Trading_OtherCancelledTitle") - local message = managers.localization:text("Trading_OtherCancelledMessage") - message = message:gsub("{1}", GoonBase.Network:GetNameFromPeerID(Trading.TradeData.Trader)) - local menuOptions = {} - menuOptions[1] = { - text = managers.localization:text("Trading_OtherCancelledAccept"), - is_cancel_button = true - } - Trading.ActiveTradeWindow = QuickMenu:new(title, message, menuOptions) - Trading.ActiveTradeWindow.dialog_data.indicator = true - Trading.ActiveTradeWindow:Show() - - -- Clear local trade data - Trading.TradeData = {} - -end - --- Windows -function Trading:CloseActiveWindow() - if Trading.ActiveTradeWindow ~= nil then - managers.system_menu:close(Trading.ActiveTradeWindow.dialog_data.id) - Trading.ActiveTradeWindow.visible = false - Trading.ActiveTradeWindow = nil - end -end - --- Weapon Inventory -function Trading:_GetWeaponTweakData(weapon) - return tweak_data.weapon[weapon] -end - -function Trading:AddWeaponToInventory(weapon, mods) - - local category = Trading.TradeData.Category or "primaries" - local freeSlot = managers.blackmarket:_get_free_weapon_slot(category) - - if freeSlot ~= nil then - - managers.blackmarket:on_buy_weapon_platform(category, weapon, freeSlot, true) - - if mods ~= nil then - - -- Keep track of mods we can't equip - local banned_mods = {} - - -- Attach mods - for k, v in pairs(mods) do - local factory = tweak_data.weapon.factory.parts[v] - if factory then - - -- Attach weapons for DLC we own, add it to the inventory for mods we dont - if factory.dlc ~= nil and not managers.dlc:has_dlc(factory.dlc) then - banned_mods[v] = factory.dlc - managers.blackmarket:add_to_inventory(factory.dlc, "weapon_mods", v, true) - else - managers.blackmarket:buy_and_modify_weapon(category, freeSlot, "normal", v, true, true) - end - - end - end - - -- Warn us about mods we can't use - for k, v in pairs(banned_mods) do - Trading:ShowModDLCNotOwned(tweak_data.weapon.factory.parts[k].name_id, tweak_data.weapon.factory.parts[k].dlc) - end - - end - - end - -end - -function Trading:ShowModDLCNotOwned(mod, dlc) - - mod = managers.localization:text(mod) - dlc = Trading.DLCNames[dlc] or ("error: " .. dlc) - - local title = managers.localization:text("Trading_TradeWindowTitle") - local message = managers.localization:text("Trading_WeaponModRemovedDLC") - message = message:gsub("{1}", mod) - message = message:gsub("{2}", dlc) - local menuOptions = {} - menuOptions[1] = { text = managers.localization:text("Trading_TradeWindowOk"), is_cancel_button = true } - local tradeMenu = QuickMenu:new(title, message, menuOptions, true) - -end - -function Trading:RemoveWeaponFromInventory() - - local category = Trading.TradeData.Slot.category - local slot = Trading.TradeData.Slot.slot - local remove_mods = not Trading.TradeData.SendWeaponMods - - -- Remove weapon and mods - local success, err = pcall(function() - managers.blackmarket:on_traded_weapon(category, slot, remove_mods) - end) - if not success then Print(err) end - - -- Reload gui - if Trading.BlackMarketGUI ~= nil then - Trading.BlackMarketGUI:reload() - end - -end - -function Trading:RemoveModFromInventory() - - managers.blackmarket:on_traded_mod(Trading.TradeData.RawData.slot.name) - -end - -function Trading:AddTradedModToInventory() - - local success, err = pcall(function() - - local mod = Trading.TradeData.WeaponMod - - -- Show message about mod - local factory = tweak_data.weapon.factory.parts[mod] - if factory then - - local mod_name = managers.localization:text(factory.name_id) - local ply_name = GoonBase.Network:GetNameFromPeerID(Trading.TradeData.Trader) - - -- Attach weapons for DLC we own, add it to the inventory for mods we dont - if factory.dlc ~= nil and not managers.dlc:has_dlc(factory.dlc) then - - local dlc_name = Trading.DLCNames[factory.dlc] or ("error: " .. factory.dlc) - - -- DLC is not owned, show different message - local title = managers.localization:text("Trading_TradeWindowTitle") - local message = managers.localization:text("Trading_WeaponModReceivedNoDLC") - message = message:gsub("{1}", mod_name) - message = message:gsub("{2}", ply_name) - message = message:gsub("{3}", dlc_name) - local menuOptions = {} - menuOptions[1] = { text = managers.localization:text("Trading_TradeWindowOk"), is_cancel_button = true } - local tradeMenu = QuickMenu:new(title, message, menuOptions, true) - - else - - -- Show default message - local title = managers.localization:text("Trading_TradeWindowTitle") - local message = managers.localization:text("Trading_WeaponModReceived") - message = message:gsub("{1}", mod_name) - message = message:gsub("{2}", ply_name) - local menuOptions = {} - menuOptions[1] = { text = managers.localization:text("Trading_TradeWindowOk"), is_cancel_button = true } - local tradeMenu = QuickMenu:new(title, message, menuOptions, true) - - end - - end - - -- Add mod to inventory - managers.blackmarket:on_received_traded_mod( Trading.TradeData.WeaponMod ) - - end) - if not success then Print(err) end - -end - -function Trading:AddTradedMaskToInventory() - - local success, err = pcall(function() - - local mask_id = Trading.TradeData.Mask - - if Trading.TradeData.MaskMods ~= nil then - - -- Adding mods to mask - local material = Trading.TradeData.MaskMods.Material - local pattern = Trading.TradeData.MaskMods.Pattern - local color = Trading.TradeData.MaskMods.Colour - - -- Check if user has all DLC for mask - local dlc_check = managers.blackmarket:has_all_dlc_for_mask_and_parts(mask_id, material, pattern, color) - if type(dlc_check) == "table" then - - -- User doesn't have all DLC, add items to inventory instead - managers.blackmarket:add_traded_mask_to_inventory(mask_id) - managers.blackmarket:add_traded_mask_part_to_inventory(material, Trading.Categories.MaskMaterial) - managers.blackmarket:add_traded_mask_part_to_inventory(pattern, Trading.Categories.MaskPattern) - managers.blackmarket:add_traded_mask_part_to_inventory(color, Trading.Categories.MaskColour) - - -- Localization strings - local mask_name = managers.blackmarket:get_mask_name( Trading.TradeData.Mask ) - local player_name = GoonBase.Network:GetNameFromPeerID( Trading.TradeData.Trader ) - local missing_dlcs = "" - - -- Show DLC warning - for k, v in pairs( dlc_check ) do - if missing_dlcs ~= "" then - missing_dlcs = missing_dlcs .. ", or " - end - missing_dlcs = (missing_dlcs .. "'{1}'"):gsub("{1}", Trading.DLCNames[k] or k) - end - Trading:ShowMaskTradeDLCWarning(mask_name, player_name, missing_dlcs) - - return - end - - -- Add modded mask to inventory - managers.blackmarket:add_traded_modded_mask_to_free_slot(mask_id, material, pattern, color) - - else - - -- Only sending mask, add to inventory - managers.blackmarket:add_traded_mask_to_inventory(mask_id) - - -- Check player owns DLC - local missing_dlc = managers.blackmarket:has_all_dlc_for_mask(mask_id) - if type(missing_dlc) == "string" then - local mask_name = managers.blackmarket:get_mask_name( Trading.TradeData.Mask ) - local player_name = GoonBase.Network:GetNameFromPeerID( Trading.TradeData.Trader ) - local dlc_name = ("'{1}'"):gsub("{1}", Trading.DLCNames[missing_dlc] or missing_dlc) - Trading:ShowMaskTradeDLCWarning(mask_name, player_name, dlc_name) - end - - end - - end) - if not success then Print(err) end - -end - -function Trading:AddTradedMaskModToInventory() - - local success, err = pcall(function() - - -- Get mod ID and check mod is valid - local mod_id = nil - local category = Trading.TradeData.Category - - if category == Trading.Categories.MaskMaterial then - mod_id = Trading.TradeData.MaskMods.Material - end - if category == Trading.Categories.MaskPattern then - mod_id = Trading.TradeData.MaskMods.Pattern - end - if category == Trading.Categories.MaskColour then - mod_id = Trading.TradeData.MaskMods.Colour - end - if mod_id == nil then - return - end - - -- Add mod to inventory - managers.blackmarket:add_traded_mask_part_to_inventory(mod_id, category) - - -- Warn about DLC - local missing_dlc = managers.blackmarket:has_all_dlc_for_mask_mod(mod_id, category) - if type(missing_dlc) == "string" then - local mod_name = managers.blackmarket:get_mask_mod_name( mod_id, category ) - local player_name = GoonBase.Network:GetNameFromPeerID( Trading.TradeData.Trader ) - local dlc_name = ("'{1}'"):gsub("{1}", Trading.DLCNames[missing_dlc] or missing_dlc) - Trading:ShowMaskTradeDLCWarning(mod_name, player_name, dlc_name) - end - - end) - if not success then Print(err) end - -end - -function Trading:ShowMaskTradeDLCWarning(mask_name, player_name, missing_dlcs) - - local title = managers.localization:text("Trading_TradeWindowTitle") - local message = managers.localization:text("Trading_MaskReceivedNoDLC") - message = message:gsub("{1}", mask_name) - message = message:gsub("{2}", player_name) - message = message:gsub("{3}", missing_dlcs) - local menuOptions = {} - menuOptions[1] = { - text = managers.localization:text("Trading_MaskReceivedAccept"), - is_cancel_button = true - } - local tradeMenu = QuickMenu:new(title, message, menuOptions, true) - -end - -function Trading:VerifyMaskTradability(data) - - -- Items which are untradable - local untradable = nil - - -- Check mask itself is fine to trade - local mask_data = managers.blackmarket:get_mask_data( data.mask_id ) - if mask_data == nil then - return - end - if mask_data.dlc ~= nil and mask_data.value == 0 then - if untradable == nil then untradable = {} end - untradable["masks"] = data.mask_id - end - - -- Check mask components are ok - if data.blueprint ~= nil then - - -- Material - local material_data = managers.blackmarket:get_mask_mod_data( data.blueprint.material.id, Trading.Categories.MaskMaterial ) - if material_data ~= nil and material_data.dlc ~= nil and material_data.value == 0 then - if untradable == nil then untradable = {} end - untradable["materials"] = data.blueprint.material.id - end - - -- Pattern - local pattern_data = managers.blackmarket:get_mask_mod_data( data.blueprint.pattern.id, Trading.Categories.MaskPattern ) - if pattern_data ~= nil and pattern_data.dlc ~= nil and pattern_data.value == 0 then - if untradable == nil then untradable = {} end - untradable["textures"] = data.blueprint.pattern.id - end - - -- Colour - local color_data = managers.blackmarket:get_mask_mod_data( data.blueprint.color.id, Trading.Categories.MaskColour ) - if color_data ~= nil and color_data.dlc ~= nil and color_data.value == 0 then - if untradable == nil then untradable = {} end - untradable["colors"] = data.blueprint.color.id - end - - end - - -- Show untradables - if untradable ~= nil then - Trading:ShowMaskTradabilityMessage(managers.blackmarket:get_mask_name(data.mask_id), untradable) - return false - end - - -- Mask is clear, good to trade - return true - -end - -function Trading:VerifyMaskModTradability(mod, category) - - local mod_data = managers.blackmarket:get_mask_mod_data( mod.name, mod.category ) - if mod_data ~= nil and mod_data.dlc ~= nil and mod_data.value == 0 then - Trading:ShowMaskModTradabilityMessage(mod.name_localized) - return false - end - return true - -end - -function Trading:ShowMaskTradabilityMessage(mask_name, items) - - local item_names = "" - for k, v in pairs( items ) do - if item_names ~= "" then - item_names = item_names .. ", and " - end - item_names = (item_names .. "'{1}'"):gsub("{1}", managers.blackmarket:get_mask_mod_name(v, k)) - end - - local title = managers.localization:text("Trading_TradeWindowTitle") - local message = managers.localization:text("Trading_UntradableMask") - message = message:gsub("{1}", mask_name) - message = message:gsub("{2}", item_names) - local menuOptions = {} - menuOptions[1] = { - text = managers.localization:text("Trading_UntradableMaskAccept"), - is_cancel_button = true - } - local tradeMenu = QuickMenu:new(title, message, menuOptions, true) - -end - -function Trading:ShowMaskModTradabilityMessage(mod_name) - - local title = managers.localization:text("Trading_TradeWindowTitle") - local message = managers.localization:text("Trading_UntradableMaskMod") - message = message:gsub("{1}", mod_name) - local menuOptions = {} - menuOptions[1] = { - text = managers.localization:text("Trading_UntradableMaskModAccept"), - is_cancel_button = true - } - local tradeMenu = QuickMenu:new(title, message, menuOptions, true) - -end - --- Fix for people who traded away their preferred character masks -function Trading:_FixPreferredCharacterMask() - - if GoonBase.Options.Trading and not GoonBase.Options.Trading.FixedPreferredCharacterMask then - - Print("[Trading] Checking if player traded away character locked mask...") - - local index = managers.blackmarket._global.crafted_items.masks[1] - local requires_reset = false - - if index then - if index.mask_id ~= "character_locked" then - requires_reset = true - end - else - requires_reset = true - end - - if requires_reset then - - Print("[Trading] Charcter locked mask not in proper place, fixing, mask in its place will be lost...") - - if not index then - index = {} - end - - index.mask_id = "character_locked" - index.global_value = "normal" - index.modded = false - index.blueprint = { - ["color"] = { - id = "nothing", - global_value = "normal", - }, - ["material"] = { - id = "plastic", - global_value = "normal", - }, - ["pattern"] = { - id = "no_color_no_material", - global_value = "normal", - }, - } - - managers.blackmarket._global.crafted_items.masks[1] = index - - else - Print("[Trading] Mask is in proper place, skipping fix") - end - - GoonBase.Options.Trading.FixedPreferredCharacterMask = true - GoonBase.Options:Save() - - end - -end From 032bfbcd613207f8ac66782a9c8106dc49b36fa5 Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Tue, 31 Mar 2015 23:42:29 +1100 Subject: [PATCH 91/92] Sentry guns and turrets will now correctly proc while instagib is active --- GoonMod/goonbase.lua | 1 + GoonMod/lua/SentryGunWeapon.lua | 14 ++++++++++++++ GoonMod/mod.txt | 3 ++- GoonMod/mutators/mutator_instagib.lua | 12 ++++++++++++ 4 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 GoonMod/lua/SentryGunWeapon.lua diff --git a/GoonMod/goonbase.lua b/GoonMod/goonbase.lua index f54b5b5..1161df0 100644 --- a/GoonMod/goonbase.lua +++ b/GoonMod/goonbase.lua @@ -86,6 +86,7 @@ GoonBase.HookFiles = { ["lib/network/networkgame"] = "NetworkGame.lua", ["lib/tweak_data/upgradestweakdata"] = "UpgradesTweakData.lua", ["lib/network/base/networkmanager"] = "NetworkManager.lua", + ["lib/units/weapons/sentrygunweapon"] = "SentryGunWeapon.lua" } diff --git a/GoonMod/lua/SentryGunWeapon.lua b/GoonMod/lua/SentryGunWeapon.lua new file mode 100644 index 0000000..dd15658 --- /dev/null +++ b/GoonMod/lua/SentryGunWeapon.lua @@ -0,0 +1,14 @@ + +CloneClass( SentryGunWeapon ) + +Hooks:RegisterHook("SentryGunWeaponOnPostSetup") +function SentryGunWeapon.setup( self, setup_data, damage_multiplier ) + self.orig.setup( self, setup_data, damage_multiplier ) + Hooks:Call( "SentryGunWeaponOnPostSetup", self, setup_data, damage_multiplier ) +end + +Hooks:RegisterHook("SentryGunWeaponOnApplyDamageMultiplier") +function SentryGunWeapon._apply_dmg_mul( self, damage, col_ray, from_pos ) + local val = Hooks:ReturnCall("SentryGunWeaponOnApplyDamageMultiplier", self, damage, col_ray, from_pos) + return val or self.orig._apply_dmg_mul( self, damage, col_ray, from_pos ) +end diff --git a/GoonMod/mod.txt b/GoonMod/mod.txt index a7d349c..de97296 100644 --- a/GoonMod/mod.txt +++ b/GoonMod/mod.txt @@ -73,6 +73,7 @@ { "hook_id" : "lib/managers/menu/skilltreegui", "script_path" : "goonbase.lua" }, { "hook_id" : "lib/network/networkgame", "script_path" : "goonbase.lua" }, { "hook_id" : "lib/tweak_data/upgradestweakdata", "script_path" : "goonbase.lua" }, - { "hook_id" : "lib/network/base/networkmanager", "script_path" : "goonbase.lua" } + { "hook_id" : "lib/network/base/networkmanager", "script_path" : "goonbase.lua" }, + { "hook_id" : "lib/units/weapons/sentrygunweapon", "script_path" : "goonbase.lua" } ] } diff --git a/GoonMod/mutators/mutator_instagib.lua b/GoonMod/mutators/mutator_instagib.lua index 4d097ca..07af5b5 100644 --- a/GoonMod/mutators/mutator_instagib.lua +++ b/GoonMod/mutators/mutator_instagib.lua @@ -9,6 +9,8 @@ BaseMutator.IsAllowedInRandomizer = false Mutator._RWChkID = "NewRaycastWeaponBase_" .. Mutator.Id Mutator._RWChkIDPost = "NewRaycastWeaponBase_PostHook_" .. Mutator.Id Mutator._NPChkID = "NPCRaycastWeaponBase_" .. Mutator.Id +Mutator._SGChkID = "SentryGunWeaponOnPostSetup_" .. Mutator.Id +Mutator._SGWepChkID = "SentryGunWeaponOnApplyDamageMultiplier_" .. Mutator.Id Hooks:Add("GoonBaseRegisterMutators", "GoonBaseRegisterMutators_" .. Mutator.Id, function() GoonBase.Mutators:RegisterMutator( Mutator ) @@ -28,6 +30,14 @@ function Mutator:OnEnabled() weapon._damage = math.huge end) + Hooks:Add("SentryGunWeaponOnPostSetup", self._SGChkID, function(sentry, setup_data, damage_multiplier) + sentry._damage = math.huge + end) + + Hooks:Add("SentryGunWeaponOnApplyDamageMultiplier", self._SGWepChkID, function(sentry, damage, col_ray, from_pos) + return math.huge + end) + end function Mutator:OnDisabled() @@ -35,5 +45,7 @@ function Mutator:OnDisabled() Hooks:Remove(self._RWChkID) Hooks:RemovePostHook(self._RWChkIDPost) Hooks:Remove(self._NPChkID) + Hooks:Remove(self._SGChkID) + Hooks:Remove(self._SGWepChkID) end From 5dcddcd62255b7ab226823359dfe31124385c9ab Mon Sep 17 00:00:00 2001 From: James Wilkinson Date: Wed, 1 Apr 2015 01:43:51 +1100 Subject: [PATCH 92/92] Fixed incorrect network library use --- GoonMod/mutators/mutator_no_ammo_pickups.lua | 2 +- GoonMod/mutators/mutator_suicide_cloakers.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/GoonMod/mutators/mutator_no_ammo_pickups.lua b/GoonMod/mutators/mutator_no_ammo_pickups.lua index e8af9d7..bf41e2d 100644 --- a/GoonMod/mutators/mutator_no_ammo_pickups.lua +++ b/GoonMod/mutators/mutator_no_ammo_pickups.lua @@ -43,7 +43,7 @@ end function Mutator:CheckSpawnAmmoBag(unit) - if GoonBase.Network:IsMultiplayer() and GoonBase.Network:IsHost() then + if LuaNetworking:IsMultiplayer() and LuaNetworking:IsHost() then self.DozersKilledSinceAmmo = self.DozersKilledSinceAmmo + 1 local dozers = self.DozersKilledSinceAmmo diff --git a/GoonMod/mutators/mutator_suicide_cloakers.lua b/GoonMod/mutators/mutator_suicide_cloakers.lua index 5868803..1d1148e 100644 --- a/GoonMod/mutators/mutator_suicide_cloakers.lua +++ b/GoonMod/mutators/mutator_suicide_cloakers.lua @@ -46,7 +46,7 @@ function Mutator:Detonate(spooc) }) managers.explosion:play_sound_and_effects(pos, math.UP, range, explosion_params) - if GoonBase.Network:IsMultiplayer() and GoonBase.Network:IsHost() then + if LuaNetworking:IsMultiplayer() and LuaNetworking:IsHost() then local grenade_type = "launcher_frag" local unit_name = Idstring(tweak_data.blackmarket.grenades[grenade_type].unit)